home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #2 / Monster Media No. 2 (Monster Media)(1994).ISO / utils1 / 2m21src.zip / 2M.ASM < prev    next >
Assembly Source File  |  1994-05-31  |  127KB  |  3,018 lines

  1.  
  2. ;┌───────────────────────────────────────────────────────────────────┐
  3. ;│                                                                   │
  4. ;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▄  ▒▒▒▒▄      ▒▒▒▒▄                                │
  5. ;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒█  ▒▒▒▒▒▄    ▒▒▒▒▒█                                │
  6. ;│  ▀▀▀▀▀▀▀▀▀▀▒▒▒▒█  ▒▒▒▒▒▒▄  ▒▒▒▒▒▒█                                │
  7. ;│            ▒▒▒▒█  ▒▒▒▒▒▒▒▄▒▒▒▒▒▒▒█                                │
  8. ;│            ▒▒▒▒█  ▒▒▒▒█▒▒▒▒▒█▒▒▒▒█                                │
  9. ;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒█  ▒▒▒▒█ ▒▒▒█▀▒▒▒▒█    ▒▒▒▒▒▒▒▒▒▒▄         ▒▒▄     │
  10. ;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒█  ▒▒▒▒█  ▒█▀ ▒▒▒▒█     ▀▀▀▀▀▀▀▒▒█       ▒▒▒▒█     │
  11. ;│ ▒▒▒▒█▀▀▀▀▀▀▀▀▀▀▀  ▒▒▒▒█   ▀  ▒▒▒▒█            ▒▒█     ▒▒▄▀▒▒█     │
  12. ;│ ▒▒▒▒█             ▒▒▒▒█      ▒▒▒▒█    ▒▒▒▒▒▒▒▒▒▒█      ▀▀ ▒▒█     │
  13. ;│ ▒▒▒▒█             ▒▒▒▒█      ▒▒▒▒█    ▒▒█▀▀▀▀▀▀▀▀         ▒▒█     │
  14. ;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▄  ▒▒▒▒█      ▒▒▒▒█    ▒▒█                 ▒▒█     │
  15. ;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒█  ▒▒▒▒█      ▒▒▒▒█    ▒▒▒▒▒▒▒▒▒▒▄ ▒▒▄ ▒▒▒▒▒▒▒▒▒▒▄ │
  16. ;│  ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀   ▀▀▀▀       ▀▀▀▀     ▀▀▀▀▀▀▀▀▀▀  ▀▀  ▀▀▀▀▀▀▀▀▀▀ │
  17. ;│                                                                   │
  18. ;│         2M 2.1  -  (C) Mayo 1994  Ciriaco García de Celis.        │
  19. ;│                                                                   │
  20. ;│      SOPORTE PARA DISQUETES CON MAYOR CAPACIDAD DE LA NORMAL      │
  21. ;│                    CREADOS POR LA UTILIDAD 2MF                    │
  22. ;│                                                                   │
  23. ;│ - Sólo para AT y superiores con controladora y unidades de alta.  │
  24. ;│ - Programación directa del controlador de disquetes y del DMA.    │
  25. ;│ - Soporte para sectores de más de 512 bytes (economizar GAPs).    │
  26. ;│ - Mezcla de sectores de distinto tamaño en la misma pista.        │
  27. ;│ - Ruptura del límite habitual de 80 pistas (incluido 360K).       │
  28. ;│ - Emulación de sectores de 512 bytes en INT 13h.                  │
  29. ;│ - Función 5 (INT 13h) reforzada para formatear los nuevos discos. │
  30. ;│ - El soporte residente opera eficazmente bajo DOS y WINDOWS 3.X   │
  31. ;│                                                                   │
  32. ;│      Emplear TASM /m5 y TLINK /t para obtener un fichero COM      │
  33. ;│                                                                   │
  34. ;└───────────────────────────────────────────────────────────────────┘
  35.  
  36.                .286                    ; versión para AT o superior
  37.  
  38. ; ------------ Macros de propósito general.
  39.  
  40. XPUSH          MACRO regmem            ; apilar lista de registros
  41.                  IRP rm, <regmem>
  42.                    PUSH rm
  43.                  ENDM
  44.                ENDM
  45.  
  46. XPOP           MACRO regmem            ; desapilar lista de registros
  47.                  IRP rm, <regmem>
  48.                    POP rm
  49.                  ENDM
  50.                ENDM
  51.  
  52. DELAY          MACRO                   ; estados de espera
  53.                  JMP SHORT $+2         ; para AT obsoleto
  54.                  JMP SHORT $+2
  55.                ENDM
  56.  
  57. PMICRO         MACRO                   ; retardo de aprox. 15,09 µs
  58.                LOCAL pmicro_iter       ; (exactamente 18/1193180 sg.)
  59. pmicro_iter:   DELAY
  60.                IN    AL,61h            ; Esta macro puede ejecutarse
  61.                AND   AL,10h            ; repetitivamente (se apoya en
  62.                CMP   AL,AH             ; AX) para hacer retardos a
  63.                JE    pmicro_iter       ; través de la temporización
  64.                MOV   AH,AL             ; del refresco de la memoria
  65.                ENDM                    ; dinámica de los AT.
  66.  
  67. ; ------------ Estructura de datos con información para cada unidad.
  68.  
  69. info_drv       STRUC
  70. maxs           EQU   13           ; máximo 13 sectores físicos/pista
  71. tipo_drv       DB    ?            ; tipo de la disquetera (0 = no hay)
  72. control2m_flag DB    OFF          ; a ON si 2M controla la unidad
  73. cambio         DB    ON           ; a ON indica cambio de soporte
  74. version_fmt    DB    ?            ; versión del formato de disco 2M
  75. multi_io       DB    ?            ; a 0 si posible acceso multi-sector
  76. chk            DB    ?            ; a 0 si checksum del sector 0 Ok
  77. vunidad        EQU   THIS WORD
  78. vunidad0       DB    ?            ; velocidad pista 0
  79. vunidadx       DB    ?            ; velocidad demás pistas
  80. gap            DB    ?            ; GAP entre sectores (leer/escribir)
  81. sectpista      DB    ?            ; sectores lógicos por pista
  82. tabla_tsect    DB    maxs DUP (?) ; tamaños de sectores 1, 2, ..., N
  83. tam_fat        DB    ?            ; sectores/FAT en la unidad
  84.                ENDS
  85.  
  86. ; ------------ Programa.
  87.  
  88. _PRINCIPAL     SEGMENT
  89.                ASSUME CS:_PRINCIPAL, DS:_PRINCIPAL
  90.  
  91.                ORG   100h
  92.  
  93. ini_residente  EQU   $
  94.  
  95. ; ****************************************
  96. ; *                                      *
  97. ; *   D A T O S    R E S I D E N T E S   *
  98. ; *                                      *
  99. ; ****************************************
  100.  
  101. inicio:        JMP   main
  102.  
  103. ; ------------ Identificación estandarizada del programa.
  104.  
  105. program_id     LABEL BYTE
  106. segmento_real  DW    0   ; segmento real donde será cargado
  107. offset_real    DW    0   ; offset real     "     "     "
  108. longitud_total DW    0   ; zona de memoria ocupada (párrafos)
  109. info_extra     DB    80h ; bits 0, 1 y 2-> 000: normal, con PSP
  110.                          ;                 001: bloque UMB XMS
  111.                          ;                 010: *.SYS
  112.                          ;                 011: *.SYS formato EXE
  113.                          ; bit 7 a 1: «extension_id» definida
  114. multiplex_id   DB    0   ; número Multiplex de este TSR
  115. vectores_id    DW    tabla_vectores
  116. extension_id   DW    tabla_extra
  117.                DB    "*##*"
  118. autor_nom_ver  DB    "CiriSOFT:2M:2.1",0
  119.  
  120.                DB    2  ; número de vectores de interrupción usados
  121. tabla_vectores EQU   $
  122. vieja_i13      DB    13h           ; INT 13h
  123. ant_int13      LABEL DWORD         ; dirección original
  124. ant_int13_off  DW    0
  125. ant_int13_seg  DW    0
  126.                DB    2Fh           ; INT 2Fh
  127. ant_int2F      LABEL DWORD         ; dirección original
  128. ant_int2F_off  DW    0
  129. ant_int2F_seg  DW    0
  130.  
  131. tabla_extra    LABEL BYTE
  132.                DW    ctrl_exterior ; permitido control exterior
  133.                DW    0             ; campo reservado
  134.  
  135. ctrl_exterior  LABEL BYTE
  136. reubicabilidad DB    1             ; programa 100% reubicable
  137. activacion     DW    act
  138. act            DB    1
  139.  
  140. ; ------------ Variables del programa.
  141.  
  142. info_ptr       DW    info_A       ; punteros a datos de las unidades
  143.                DW    info_B
  144. id_sistema     DB    "2M-STV"     ; identificación de disco 2M
  145. unidad         DB    ?            ; unidad física de disco en curso
  146. numsect        DW    ?            ; sectores a transferir
  147. sectini        DW    ?            ; primer sector DOS a transferir
  148. cilindro       DB    ?            ; cilindro del disco a acceder
  149. cabezal        DB    ?            ; cabezal a emplear
  150. sector         DB    ?            ; número de sector físico
  151. sector_ini     DB    ?            ; número de sector físico inicial
  152. sector_fin     DB    ?            ; número de sector físico final
  153. seccion        DB    ?            ; parte del sector físico en curso
  154. secciones      DB    ?            ; sectores lógicos a transferir
  155. tsector        DB    ?            ; LOG2 (tamaño de sector) - 7
  156. buffer         DW    buffer_io    ; puntero al buffer intermedio
  157. buf_unidad     DB    ?            ; unidad del sector en el buffer
  158. buf_cilcab     DW    ?            ; cilindro/cabezal de sector buffer
  159. buf_sector     DB    ?            ; número de sector en el buffer
  160. status         DB    ?            ; resultado de los accesos a disco
  161. fdc_result     DB    7 DUP (?)    ; bytes de resultados del FDC
  162. orden          DB    ?            ; operación F_READ/F_WRITE/F_VERIFY
  163. tab_ordenes    DB    F_READ
  164.                DB    F_WRITE
  165.                DB    F_VERIFY     ; órdenes 2, 3 y 4
  166.  
  167.                ; --- Interpretación BIOS de los bits de ST1
  168.  
  169. lista_errs     DB    4            ; 'sector not found'
  170.                DB    0
  171.                DB    10h          ; 'bad CRC'
  172.                DB    8            ; 'DMA overrun'
  173.                DB    0
  174.                DB    4            ; 'sector not found'
  175.                DB    3            ; 'write-protect error'
  176.                DB    2            ; 'address mark not found'
  177.                DB    20h          ; en otro caso: 'bad NEC'
  178.  
  179. info_A         info_drv <>        ; datos de A:
  180. info_B         info_drv <>        ; datos de B:
  181.  
  182. ; ***************************************
  183. ; *                                     *
  184. ; *   C O D I G O   R E S I D E N T E   *
  185. ; *                                     *
  186. ; ***************************************
  187.  
  188. ; ------------ Rutina de gestión de INT 2Fh.
  189.  
  190. ges_int2F      PROC  FAR
  191.                STI
  192.                CMP   AH,CS:multiplex_id
  193.                JE    preguntan
  194.                JMP   CS:ant_int2F      ; saltar al gestor de INT 2Fh
  195. preguntan:     CMP   DI,1992h
  196.                JNE   ret_no_info       ; no llama alguien del convenio
  197.                MOV   AX,ES
  198.                CMP   AX,1492h
  199.                JNE   ret_no_info       ; no llama alguien del convenio
  200.                PUSH  CS
  201.                POP   ES                ; sí llama: darle información
  202.                LEA   DI,autor_nom_ver
  203. ret_no_info:   MOV   AX,0FFFFh         ; "entrada multiplex en uso"
  204.                IRET
  205. ges_int2F      ENDP
  206.  
  207. ; ------------ Nueva rutina de gestión de INT 13h. Llama a la INT 13h
  208. ;              original o a una nueva rutina de control para la
  209. ;              lectura (AH=2), escritura (AH=3) y verificación (AH=4)
  210. ;              según el tipo de disco introducido. Ante una función de
  211. ;              formateo (AH=5) se entrega el control a la INT 13h
  212. ;              original. Se detecta un posible cambio de disco y se
  213. ;              retorna en ese caso con el correspondiente error.
  214.  
  215. ges_int13      PROC  FAR
  216.                STI
  217.                CLD
  218.                PUSHF
  219.                CMP   DL,2
  220.                JAE   ges13bios         ; no es disquetera A: ó B:
  221.                PUSH  SI
  222.                CALL  set_SI_drv
  223.                CMP   CS:[SI].tipo_drv,2  ; ¿unidad 1.2M?
  224.                JE    ges_2m
  225.                CMP   CS:[SI].tipo_drv,4  ; ¿unidad 1.44/2.88M?
  226. ges_2m:        POP   SI
  227.                JC    ges13bios         ; no es unidad de alta densidad
  228.                CMP   AH,2
  229.                JB    ges13bios         ; no Read/Write/Verify/Format
  230.                CMP   AH,5
  231.                JA    ges13bios         ; no Read/Write/Verify/Format
  232.                CALL  detecta_cambio    ; ¿cambio de disco?
  233.                JNC   sin_cambio
  234.                POPF
  235.                STC                     ; hubo cambio:
  236.                MOV   AX,600h
  237.                RET   2                 ; retornar con error
  238. sin_cambio:    CMP   AH,5
  239.                JNE   dilucida          ; no es orden de formateo
  240.                CALL  leer_lin_camb
  241.                JNZ   format_bios       ; no hay disquete en la unidad
  242.                CMP   AL,7Fh
  243.                JNE   format_bios       ; no es orden formateo de 2M
  244.                CMP   SI,"2M"
  245.                JE    format_2m         ; es orden de formateo de 2M
  246. format_bios:   CLC
  247.                CALL  set_flag_STV      ; CF = 0 -> indicar no 2M
  248. dilucida:      PUSH  SI
  249.                CALL  set_SI_drv        ; SI -> variables de la unidad
  250.                CMP   CS:[SI].control2m_flag,OFF
  251.                POP   SI
  252.                JE    ges13bios         ; la unidad la controla la BIOS
  253.                POPF
  254.                CALL  control2m         ; la controla 2M
  255.                RET   2
  256. ges13bios:     POPF
  257.                JMP   CS:ant_int13      ; saltar al gestor de INT 13h
  258.  
  259.                ; --- Función de formateo implementada por 2M. En los
  260.                ;     disquetes creados con /M todas las pistas salvo
  261.                ;     la 0 deberían ser formateadas invocando INT 13h
  262.                ;     de manera directa (con CALL) para evitar que se
  263.                ;     ejecute cierto código de WINDOWS en el modo
  264.                ;     protegido que provoca errores al formatear. Antes
  265.                ;     de formatear la primera pista física del disco se
  266.                ;     invoca la función de formateo de la INT 13h
  267.                ;     original (con un error para provocar un rápido
  268.                ;     retorno) con objeto de informar al DOS y a todos
  269.                ;     los TSR previos del cambio de soporte.
  270.  
  271. format_2m:     POPF
  272.                PUSH  DS                ; *
  273.                PUSHA                   ; **
  274.                PUSH  CS
  275.                POP   DS
  276.                MOV   unidad,DL
  277.                CALL  set_SI_drv
  278.                MOV   cilindro,CH
  279.                MOV   cabezal,DH
  280.                OR    CH,DH
  281.                JNZ   format_trx        ; no es cilindro 0 y cabezal 0
  282.                PUSHA
  283.                MOV   AL,1              ; formatear un sector (AH=5)
  284.                MOV   CH,0
  285.                MOV   DH,2              ; en cabezal 2 (incorrecto)
  286.                PUSHF
  287.                CALL  ant_int13         ; avisar al DOS del nuevo disco
  288.                CLD                     ; mantener DF=0
  289.                STC
  290.                CALL  reset_drv         ; asegurar aceleración motor
  291.                POPA
  292.                CALL  set_info          ; características nuevo soporte
  293. format_trx:    CALL  genera_info       ; tabla de información formateo
  294.                CALL  motor_ok          ; asegurar que está en marcha
  295.                CALL  seek_drv
  296.                CALL  formatea_pista
  297.                PUSHF
  298.                CLC
  299.                CALL  motor_off_cnt     ; cuenta normal detención motor
  300.                POPF
  301.                CALL  set_err
  302.                CALL  set_bios_err      ; no altera flags
  303.                POPA                    ; **
  304.                MOV   AH,status
  305.                POP   DS                ; *
  306.                RET   2
  307. ges_int13      ENDP
  308.  
  309. ; ------------ A la entrada en DL se indica la unidad y a la salida se
  310. ;              devuelve SI apuntando sus variables sin alterar flags.
  311.  
  312. set_SI_drv     PROC
  313.                PUSHF
  314.                PUSH  BX
  315.                MOV   BL,DL
  316.                MOV   BH,0
  317.                SHL   BX,1
  318.                MOV   SI,CS:[BX+OFFSET info_ptr]
  319.                POP   BX
  320.                POPF
  321.                RET
  322. set_SI_drv     ENDP
  323.  
  324. ; ------------ Si CF=1, indicar disquete 2M presente. A la
  325. ;              entrada, DL indica la unidad de disco.
  326.  
  327. set_flag_STV   PROC
  328.                PUSHA
  329.                CALL  set_SI_drv
  330.                MOV   AL,ON                       ; indicar 2M
  331.                JC    tipo_stv_ok
  332.                MOV   AL,OFF                      ; indicar no 2M
  333. tipo_stv_ok:   MOV   CS:[SI].control2m_flag,AL
  334.                POPA
  335.                RET
  336. set_flag_STV   ENDP
  337.  
  338. ; ------------ Devolver ZF=1 si cilindro y cabezal 0.
  339.  
  340. pista0?        PROC
  341.                PUSH  AX
  342.                MOV   AL,cabezal
  343.                OR    AL,cilindro
  344.                POP   AX
  345.                RET
  346. pista0?        ENDP
  347.  
  348. ; ------------ Devolver ZF=1 si la línea de cambio de disco está
  349. ;              inactiva. A la entrada, DL contiene la unidad. El
  350. ;              motor es puesto en marcha y, si no lo estaba ya, la
  351. ;              variable que indica lo que resta para detenerlo
  352. ;              es llevada a su valor normal, por lo que el disco no
  353. ;              tardará mucho en detenerse (incluso sin quizá haber
  354. ;              acelerado aún). En la práctica, invocando esta rutina
  355. ;              desde INT 13h nunca será necesario arrancar el motor
  356. ;              ya que el DOS ejecuta antes la función equivalente,
  357. ;              la 16h, que lo pone en marcha. Es simplemente una
  358. ;              medida de seguridad contra las BIOS «de marca».
  359.  
  360. leer_lin_camb  PROC
  361.                PUSHA                   ; *
  362.                PUSH  DS
  363.                PUSH  40h
  364.                POP   DS
  365.                MOV   AL,1
  366.                MOV   CL,DL
  367.                SHL   AL,CL             ; bit de motor en 0..3
  368.                TEST  DS:[3Fh],AL
  369.                JNZ   rodando           ; el motor ya está girando
  370.                CLC
  371.                CALL  motor_off_cnt     ; cuenta normal detención motor
  372. rodando:       MOV   AH,DL
  373.                SHL   AH,4
  374.                OR    AH,AL             ; AH = byte BIOS
  375.                SHL   AL,4
  376.                OR    AL,00001100b      ; modo DMA, no hacer reset
  377.                OR    AL,DL             ; AL para reg. salida digital
  378.                MOV   DX,3F2h
  379.                CLI
  380.                MOV   DS:[3Fh],AH       ; actualizar variable BIOS
  381.                OUT   DX,AL             ; arrancado motor en la unidad
  382.                ADD   DX,5
  383.                DELAY
  384.                IN    AL,DX             ; leer línea de cambio de disco
  385.                STI
  386.                TEST  AL,80h            ; ZF=0 -> cambio de disco
  387.                POP   DS
  388.                POPA                    ; *
  389.                RET
  390. leer_lin_camb  ENDP
  391.  
  392. ; ------------ Determinar si ha habido cambio de disco y, en ese caso,
  393. ;              si el nuevo disquete es de tipo 2M o no. El cambio de
  394. ;              disco se detecta leyendo la línea de cambio de disco o
  395. ;              chequeando la variable que indica si ha habido cambio
  396. ;              o no (esta variable está a ON tras instalar 2M para
  397. ;              forzar la detección del tipo de disco introducido; se
  398. ;              pone en ON también si no se logra bajar la línea de
  399. ;              cambio de disco por si fuera un soporte raro y la BIOS
  400. ;              sí lo lograra -forzando así una detección posterior-).
  401.  
  402. detecta_cambio PROC
  403.                PUSHA                   ; *
  404.                CALL  set_SI_drv        ; SI -> variables de la unidad
  405.                CMP   CS:[SI].cambio,ON ; ¿cambio de soporte?
  406.                MOV   CS:[SI].cambio,OFF
  407.                JE    hubo_cambio
  408.                CALL  leer_lin_camb     ; leer línea de cambio de disco
  409.                JNZ   hubo_cambio
  410.                POPA
  411.                CLC                     ; no hay cambio de disco
  412.                RET
  413. hubo_cambio:   CLC
  414.                CALL  set_flag_STV      ; CF = 0 -> supuesto no 2M
  415.                XPUSH <DS, ES>          ; **
  416.                MOV   BX,90h
  417.                ADD   BL,DL
  418.                PUSH  40h
  419.                POP   DS
  420.                AND   BYTE PTR [BX],255-16 ; densidad no determinada
  421.                XPUSH <CS, CS>
  422.                XPOP  <DS, ES>
  423.                MOV   unidad,DL
  424.                STC                     ; asegurar motor en marcha
  425.                CALL  reset_drv
  426.                MOV   cilindro,1
  427.                MOV   cabezal,0
  428.                CALL  seek_drv          ; bajar línea cambio de disco
  429.                DEC   cilindro
  430.                CALL  seek_drv
  431.                CLC
  432.                CALL  motor_off_cnt     ; cuenta normal detención motor
  433.                CALL  leer_lin_camb     ; ¿bajada línea cambio disco?
  434.                JZ    disco_dentro      ; se pudo: hay disco dentro
  435.                MOV   [SI].cambio,ON    ; futura detección tipo disco
  436.                CLC                     ; NO indicar cambio de disco...
  437.                JMP   fin_detecta       ; ...para pasar control a BIOS
  438. disco_dentro:  PUSH  DS
  439.                PUSH  40h
  440.                POP   DS
  441.                MOV   BYTE PTR DS:[41h],6  ; error 'media changed'
  442.                POP   DS
  443.                CMP   AH,5              ; ¿función de formateo?
  444.                JE    fin_detecta_c     ; no perder el tiempo
  445.                MOV   buf_unidad,-1     ; invalidar buffer
  446.                MOV   [SI].gap,20       ; GAP provisional
  447.                MOV   CX,3              ; 3 intentos
  448. intenta_io0:   PUSH  CX
  449.                CMP   CX,2              ; CF=1 la 3ª vez (a 0 si CX<>1)
  450.                CALL  reset_drv
  451.                MOV   [SI].vunidad0,0   ; empezar con 500 Kbit/seg.
  452. intenta_io:    MOV   cilindro,0
  453.                MOV   cabezal,0
  454.                MOV   sector,1          ; sector de arranque
  455.                MOV   seccion,0
  456.                MOV   secciones,1
  457.                MOV   orden,F_READ
  458.                MOV   DI,buffer
  459.                CALL  direct_acceso
  460.                JNE   otra_densidad     ; es otra densidad de disco
  461.                POP   CX
  462.                MOV   BX,buffer
  463.                CALL  set_info          ; características nuevo soporte
  464.                CLC
  465.                JMP   fin_detecta_c     ; indicar cambio de disco
  466. otra_densidad: MOV   AL,[SI].vunidad0
  467.                INC   AX                ; próxima velocidad
  468.                CMP   AL,3
  469.                JA    otro_intento
  470.                MOV   [SI].vunidad0,AL
  471.                JMP   intenta_io        ; probar otra velocidad
  472. otro_intento:  MOV   [SI].vunidad0,0
  473.                POP   CX
  474.                LOOP  intenta_io0       ; reintento
  475. fin_detecta_c: STC                     ; indicar cambio de disco
  476. fin_detecta:   XPOP  <ES, DS>          ; **
  477.                POPA                    ; *
  478.                RET
  479. detecta_cambio ENDP
  480.  
  481. ; ------------ Anotar la información del disquete si es de tipo 2M.
  482. ;              A la entrada, DS:SI apunta a las variables de la unidad
  483. ;              y ES:BX al sector de arranque del disco. Se actualiza
  484. ;              también la variable BIOS de tipo de densidad (la BIOS
  485. ;              no se da cuenta del cambio de disco y conviene ayudar).
  486.  
  487. set_info       PROC
  488.                PUSHA
  489.                CALL  calc_chk
  490.                JC    set_info_exit     ; no es disco 2M
  491.                MOV   [SI].chk,AL         ; anotar checksum
  492.                MOV   [SI].version_fmt,CL ; y versión del formato
  493.                MOV   DL,unidad
  494.                STC
  495.                CALL  set_flag_STV      ; CF = 1 -> indicar disco 2M
  496.                MOV   AL,ES:[BX+22]     ; tamaño de FAT
  497.                MOV   [SI].tam_fat,AL
  498.                MOV   CL,ES:[BX+65]     ; CL a 0 si acceso multi-sector
  499.                MOV   [SI].multi_io,CL
  500.                MOV   AX,ES:[BX+66]
  501.                MOV   [SI].vunidad,AX   ; velocidad pista 0 / demás
  502.                MOV   AL,ES:[BX+24]
  503.                MOV   [SI].sectpista,AL ; sectores/pista
  504.                MOV   DI,ES:[BX+72]
  505.                MOV   AL,ES:[BX+DI+1]   ; GAP de formateo
  506.                MOV   AH,AL
  507.                AND   CL,CL             ; CL a 0 si acceso multi-sector
  508.                JZ    gap_rw_ok         ; GAP R/W para /F
  509.                ADD   AH,190
  510.                MOV   AL,11
  511.                MUL   AH                ; AX = (190+GAP)*11
  512.                SUB   AX,2048+62
  513. gap_rw_ok:     SHR   AL,1              ; GAP R/W para /M
  514.                MOV   [SI].gap,AL
  515.                MOV   CX,maxs
  516.                MOV   DI,ES:[BX+74]
  517.                ADD   DI,BX
  518.                LEA   BX,[SI].tabla_tsect
  519. genera_ts:     MOV   AL,ES:[DI]
  520.                MOV   [BX],AL
  521.                INC   BX
  522.                INC   DI
  523.                LOOP  genera_ts         ; información estructura pistas
  524. set_info_exit: MOV   AL,[SI].vunidad0
  525.                SHL   AL,6              ; velocidad en bits 7:6
  526.                OR    AL,00010111b      ; establecido otro medio físico
  527.                CMP   [SI].tipo_drv,2
  528.                JA    modo_ok           ; es unidad de 3½
  529.                AND   AL,11111000b
  530.                OR    AL,00000101b      ; 1.2 en 1.2
  531.                TEST  AL,01000000b
  532.                JZ    modo_ok
  533.                XOR   AL,00100001b      ; 360 en 1.2 y seek * 2
  534. modo_ok:       PUSH  DS
  535.                MOV   BX,90h
  536.                ADD   BL,unidad
  537.                PUSH  40h
  538.                POP   DS
  539.                AND   BYTE PTR DS:[BX],8  ; respetar bit de 2.88M
  540.                OR    DS:[BX],AL        ; actualizar variable BIOS
  541.                POP   DS                ; con el tipo de densidad
  542.                POPA
  543.                RET
  544. set_info       ENDP
  545.  
  546. ; ------------ Calcular el checksum de la zona vital del sector de
  547. ;              arranque. A la entrada, ES:BX -> sector de arranque.
  548. ;              A la salida, CF=1 si el disco no es 2M; de otro modo
  549. ;              checksum en AL y versión del formato de disco en CL.
  550.  
  551. calc_chk       PROC
  552.                XPUSH <SI, DI>
  553.                LEA   DI,[BX+3]         ; DI=BX+3
  554.                LEA   SI,id_sistema
  555.                MOV   CX,6
  556.                REP   CMPSB             ; comparar identificación
  557.                STC
  558.                JNE   chk_ret           ; el disco no es 2M
  559.                XOR   AX,AX
  560.                MOV   CL,ES:[BX+64]     ; versión del formateador
  561.                CMP   CL,6
  562.                JB    chk_ok            ; no usaba este checksum
  563.                MOV   DI,ES:[BX+68]
  564. chk_sum:       DEC   DI
  565.                ADD   AL,ES:[BX+DI]
  566.                CMP   DI,63
  567.                JA    chk_sum
  568. chk_ok:        CLC
  569. chk_ret:       XPOP  <DI, SI>
  570.                RET
  571. calc_chk       ENDP
  572.  
  573. ; ------------ Determinar el tipo de error producido en el acceso.
  574.  
  575. set_err        PROC
  576.                PUSHA
  577.                JNC   err_ret           ; no hay error
  578.                CMP   status,0          ; ¿'status' ya asignado?
  579.                JNE   err_retc          ; no cambiarlo si es así
  580.                MOV   AL,BYTE PTR fdc_result+1
  581.                AND   AL,10110111b      ; aislar condiciones de test
  582.                LEA   BX,lista_errs
  583.                MOV   CX,9
  584. busca_err:     MOV   AH,[BX]           ; código de error BIOS
  585.                SHL   AL,1
  586.                JC    err_ok            ; es ese error
  587.                INC   BX
  588.                LOOP  busca_err         ; buscar otro error
  589. err_ok:        OR    status,AH
  590. err_retc:      STC                     ; condición de error
  591. err_ret:       POPA
  592.                RET
  593. set_err        ENDP
  594.  
  595. ; ------------ Actualizar variables de error de la BIOS.
  596.  
  597. set_bios_err   PROC
  598.                PUSHF                   ; *
  599.                PUSHA                   ; **
  600.                PUSH  ES                ; ***
  601.                PUSH  40h
  602.                POP   ES
  603.                MOV   DI,41h            ; bytes de resultados del 765
  604.                LEA   SI,status         ; variable BIOS de status y 7
  605.                MOV   CX,4              ; bytes: 4 palabras
  606.                REP   MOVSW
  607.                POP   ES                ; ***
  608.                POPA                    ; **
  609.                POPF                    ; *
  610.                RET
  611. set_bios_err   ENDP
  612.  
  613. ; ------------ Realizar lecturas, escrituras y verificaciones: rutina
  614. ;              que sustituye el código de la BIOS para poder soportar
  615. ;              los formatos 2M. La operación puede quedar dividida en
  616. ;              tres fases: el fragmento anterior a la FAT2, la zona
  617. ;              correspondiente a la FAT2 (se ignora la escritura y se
  618. ;              simula su lectura leyendo la FAT1) y un último bloque
  619. ;              ubicado tras la FAT2. El sector de arranque es emulado
  620. ;              empleando el primer sector físico de la FAT2 (aunque en
  621. ;              los discos de versión de formato anterior a la 7 se usa
  622. ;              el sector de arranque verdadero -permitiendo escribirlo
  623. ;              sólo si es válido-). En cualquier caso, si el número de
  624. ;              cabezal tiene el bit 7 activo, se sobreentiende que el
  625. ;              programa que llama soporta disquetes 2M y no se emula
  626. ;              la FAT2 ni el sector de arranque, para permitirle
  627. ;              acceder al código SuperBOOT. Las coordenadas de la BIOS
  628. ;              se traducen a las unidades del DOS por mayor comodidad.
  629.  
  630. control2m      PROC
  631.                PUSH  DS                ; *
  632.                PUSHA                   ; **
  633.                PUSH  CS
  634.                POP   DS
  635.                MOV   unidad,DL
  636.                CALL  set_SI_drv        ; SI -> variables de la unidad
  637.                CMP   [SI].chk,0
  638.                JE    chk_valido        ; checksum correcto en sector 0
  639.                MOV   status,40h        ; devolver 'Seek Error' al DOS
  640.                JMP   exit_2m_ctrl
  641. chk_valido:    PUSH  AX                ; ***
  642.                MOV   AH,0
  643.                MOV   numsect,AX        ; nº sectores
  644.                MOV   AL,CH             ; cilindro
  645.                SHL   AL,1
  646.                MOV   DL,DH
  647.                AND   DH,01111111b
  648.                ADD   AL,DH             ; cabezal físico
  649.                MUL   [SI].sectpista
  650.                ADD   AL,CL             ; sector
  651.                ADC   AH,0
  652.                DEC   AX                ; AX = nº sector DOS
  653.                MOV   sectini,AX
  654.                MOV   DI,BX             ; ES:DI -> dirección
  655.                POP   BX                ; ***
  656.                MOV   BL,BH
  657.                MOV   BH,0
  658.                MOV   CL,[BX+OFFSET tab_ordenes-2]
  659.                MOV   orden,CL
  660.                SHL   DL,1
  661.                JC    acceso_final      ; cabezal >= 128: no emular
  662.                AND   AX,AX             ; ¿comienza en sector 0?
  663.                JNZ   io_emula          ; no
  664.                CMP   [SI].version_fmt,7
  665.                JB    boot_real         ; no soportado BOOT virtual
  666.                MOV   AL,[SI].tam_fat   ; AH = 0
  667.                INC   AX
  668.                MOV   CX,1              ; sector BOOT emulado en
  669.                CALL  ejecuta_io        ; el primer sector FAT2
  670.                JNE   fin_ctrl
  671. boot_fin_op:   DEC   numsect
  672.                INC   sectini
  673.                MOV   AX,sectini
  674.                JMP   io_emula
  675. boot_real:     CMP   orden,F_WRITE
  676.                JNE   io_emula
  677.                MOV   BX,DI             ; BOOT de 2M 1.3 y anteriores
  678.                CALL  calc_chk
  679.                JC    si_skip           ; no es de tipo 2M
  680.                AND   AL,AL
  681.                JZ    io_emula          ; lo es y con checksum correcto
  682. si_skip:       ADD   DI,512
  683.                JMP   boot_fin_op       ; impedir estropicio de BOOT
  684. io_emula:      MOV   CL,[SI].tam_fat
  685.                MOV   CH,0              ; CX = primer sector FAT2 - 1
  686.                CMP   AX,CX
  687.                JA    en_fat2?          ; ¿la operación afecta a FAT2?
  688.                CALL  calc_iop          ; calcular sectores antes FAT2
  689.                CALL  ejecuta_io        ; CX sectores desde AX
  690.                JNE   fin_ctrl          ; error
  691.                CMP   numsect,0
  692.                JE    fin_ctrl          ; fin de la transferencia
  693. en_fat2?:      MOV   AX,sectini
  694.                MOV   CL,[SI].tam_fat
  695.                MOV   CH,0
  696.                SHL   CX,1              ; CX = último sector FAT2
  697.                CMP   AX,CX
  698.                JA    acceso_final      ; la operación es tras la FAT2
  699.                CALL  calc_iop          ; sectores hasta fin de FAT2
  700.                CMP   orden,F_WRITE
  701.                JNE   emula_fat1
  702.                SHL   CX,9              ; CX = CX * 512
  703.                ADD   DI,CX             ; ES:DI actualizado
  704.                JMP   acceso_final
  705. emula_fat1:    MOV   DL,[SI].tam_fat
  706.                MOV   DH,0
  707.                SUB   AX,DX             ; leer de FAT1 y no de la FAT2
  708.                CALL  ejecuta_io        ; CX sectores desde AX
  709.                JNE   fin_ctrl          ; error
  710. acceso_final:  CMP   numsect,0
  711.                JE    fin_ctrl          ; fin de la transferencia
  712.                MOV   AX,sectini
  713.                MOV   CX,numsect
  714.                CALL  ejecuta_io
  715. fin_ctrl:      CLC
  716.                CALL  motor_off_cnt     ; cuenta normal detención motor
  717.                CALL  set_bios_err      ; actualizar variables BIOS
  718. exit_2m_ctrl:  POPA                    ; **
  719.                MOV   AH,status
  720.                POP   DS                ; *
  721.                AND   AH,AH
  722.                JZ    st_ok             ; resultado correcto (CF=0)
  723.                STC                     ; error
  724.                MOV   AL,0              ; 0 sectores movidos
  725. st_ok:         RET
  726. calc_iop:      SUB   CX,AX
  727.                INC   CX                ; CX sectores
  728.                CMP   CX,numsect
  729.                JBE   nsect_ok
  730.                MOV   CX,numsect        ; sólo quedan CX
  731. nsect_ok:      SUB   numsect,CX
  732.                ADD   sectini,CX
  733.                RET
  734. control2m      ENDP
  735.  
  736. ; ------------ A la entrada, AX indica el sector inicial (coordenadas
  737. ;              del DOS) y CX el número de sectores a procesar.
  738. ;              * Definiciones: «Sector físico» es un sector del disco
  739. ;              de 512, 1024 ó 2048 bytes (números de sector del 1 al N
  740. ;              en la pista). Este sector físico está dividido en
  741. ;              «secciones» de 512 bytes, constando por tanto de 1, 2 ó
  742. ;              4 secciones. «Sector virtual» es el número de sector
  743. ;              del programa que llama a INT 13h, comprendido entre 1 y
  744. ;              M. Esta estructura de N sectores por pista de distintos
  745. ;              tamaños, se verifica en todo el disco con excepción del
  746. ;              cabezal y cilindro 0 (con un formato más convencional
  747. ;              de sectores de 512 bytes numerados de 1 a J, aunque no
  748. ;              existen algunos de los intermedios que corresponden a
  749. ;              la segunda copia de la FAT).
  750. ;              * Primero se convierte el sector virtual (1..M) en su
  751. ;              correspondiente físico (1..J en la pista 0 y 1..N en
  752. ;              las demás), deduciendo qué porción de 512 bytes (o
  753. ;              sección) es afectada. Un sector virtual (512 bytes)
  754. ;              simulado suele ser parte de un sector físico de 2048
  755. ;              bytes en muchos casos. Si dicho sector físico ya había
  756. ;              sido leído al buffer en anteriores accesos, se extrae
  757. ;              la sección necesaria. Si no, se carga del disco y se
  758. ;              extrae dicho fragmento. El número de sectores virtuales
  759. ;              que se solicitan (=secciones) permite realizar un bucle
  760. ;              hasta completar la transferencia; el interleave 1:2 de
  761. ;              los sectores físicos en /M permite acceder sector a
  762. ;              sector sin pérdida de rendimiento. En el caso de la
  763. ;              escritura, se estudia primero si hay varios sectores
  764. ;              virtuales consecutivos que escribir, completando entre
  765. ;              todos un sector físico: en ese caso, se prepara el
  766. ;              mismo y se escribe sin más. En caso de que haya que
  767. ;              modificar sólo una única sección de un sector físico,
  768. ;              salvo si éste es de 512 bytes, no hay más remedio que
  769. ;              cargarlo al buffer (realizar una prelectura),
  770. ;              actualizar la sección correspondiente y volverlo a
  771. ;              escribir.
  772. ;              * En el formato /F se realiza una operación multisector
  773. ;              si es posible y sin emplear el buffer intermedio (si
  774. ;              bien podría ser preciso emplearlo con la primera y
  775. ;              última sección); en los dos formatos de disco se hace
  776. ;              la operación multisector en la primera pista. Las
  777. ;              operaciones multisector puede que sea preciso
  778. ;              dividirlas en tres fases: los sectores antes de una
  779. ;              frontera de DMA, el que la cruza (que es transferido
  780. ;              a través del buffer intermedio) y los que están detrás.
  781.  
  782. ejecuta_io     PROC
  783.                MOV   BX,AX             ; AX = sector DOS inicial
  784.                MOV   secciones,CL      ; CX sectores (CL realmente)
  785.                DIV   [SI].sectpista
  786.                INC   AH                ; numerado desde 1...
  787.                MOV   sector,AH         ; ...el resto es el sector
  788.                SHR   AL,1
  789.                MOV   cilindro,AL       ; cilindro
  790.                RCL   AL,1
  791.                AND   AL,1
  792.                MOV   cabezal,AL        ; cabezal
  793.                MOV   AL,sector
  794.                ADD   AL,secciones
  795.                JC    no_cabe           ; sector+secciones > 255
  796.                DEC   AX                ; DEC AX = DEC AL
  797.                CMP   AL,[SI].sectpista
  798.                JBE   si_cabe
  799. no_cabe:       MOV   status,4          ; 'sector no encontrado'
  800.                JMP   fin_io
  801. si_cabe:       MOV   AL,AH             ; sector en AL
  802.                CBW                     ; sección 0 (AH = 0)
  803.                CALL  pista0?
  804.                JZ    s_xx              ; sector físico en pista/cara 0
  805.                LEA   BX,[SI].tabla_tsect-1
  806.                DEC   AX                ; AH = 0
  807. resta_secc:    INC   BX
  808.                INC   AH
  809.                MOV   CL,[BX]
  810.                SUB   CL,2
  811.                MOV   CH,1
  812.                SHL   CH,CL
  813.                SUB   AL,CH
  814.                JNC   resta_secc        ; en las demás pistas
  815.                ADD   AL,CH
  816.                XCHG  AH,AL
  817. s_xx:          MOV   sector,AL         ; sector lógico convertido a
  818.                MOV   seccion,AH        ; sector y sección físicas
  819. direct_acceso: CALL  motor_ok          ; asegurar que está en marcha
  820.                MOV   AH,0
  821.                MOV   sector_fin,AH     ; no acceder a más de 1 sector
  822.                CALL  pista0?           ; (al menos de momento)
  823.                JNZ   decide_multi      ; no es pista 0
  824.                MOV   AL,secciones
  825.                MOV   secciones,AH      ; las que restan (AH = 0)
  826.                JMP   multi_proc
  827. decide_multi:  CMP   [SI].multi_io,AH  ; AH = 0
  828.                JNE   io_pasos          ; acceso sector a sector
  829.                CMP   seccion,AH
  830.                JE    multi_acc
  831.                CALL  acceso_secc       ; no acceso a inicio sector
  832.                JC    fin_io
  833. multi_acc:     CMP   secciones,AH      ; AH = 0
  834.                JE    fin_io
  835.                CALL  num_secciones
  836.                MOV   CL,AL
  837.                MOV   AL,secciones      ; AH = 0
  838.                DIV   CL
  839.                AND   AL,AL
  840.                JZ    io_pasos          ; no quedan sectores enteros
  841.                MOV   secciones,AH      ; las que restan
  842. multi_proc:    CALL  acceso_multi      ; de AL sectores
  843.                JC    fin_io
  844. io_pasos:      CMP   secciones,0
  845.                JE    fin_io            ; no restan secciones finales
  846.                CALL  acceso_secc
  847.                JNC   io_pasos
  848. fin_io:        CMP   status,0          ; ZF = 1 -> operación correcta
  849.                RET
  850.  
  851. acceso_secc:   PUSH  AX
  852.                CMP   orden,F_WRITE     ; acabar transferencia sector
  853.                JE    escritura
  854.                CMP   orden,F_VERIFY
  855.                JE    verificacion
  856.                CALL  leido?            ; realizar lectura...
  857.                JNC   ya_leido          ; sector ya en el buffer
  858. hay_que_leer:  CALL  acceso_sector     ; efectuar E/S
  859.                JC    acc_ret           ; ha habido fallo
  860. ya_leido:      CALL  trans_secc        ; buffer -> memoria
  861.                JMP   acc_ret
  862. escritura:     CMP   seccion,0
  863.                JNE   prelectura        ; sólo parte del sector cambia
  864.                CALL  num_secciones
  865.                CMP   secciones,AL
  866.                JAE   escribir          ; Todo el sector físico cambia
  867. prelectura:    CALL  leido?            ; Leer el sector físico para
  868.                JNC   escribir          ; cambiar sólo una parte de él
  869.                MOV   orden,F_READ      ; de momento leer...
  870.                CALL  acceso_sector     ; efectuar E/S
  871.                MOV   orden,F_WRITE     ; ... restaurar orden original
  872.                JC    acc_ret           ; ha habido fallo
  873. escribir:      CALL  trans_secc        ; memoria -> buffer
  874.                CALL  acceso_sector     ; volcar buffer al disco
  875.                JMP   acc_ret
  876. verificacion:  PUSH  BX
  877.                MOV   BL,seccion
  878.                CALL  num_secciones
  879. dec_sec_veri:  DEC   secciones
  880.                JZ    verifica
  881.                INC   BX
  882.                CMP   BL,AL
  883.                JB    dec_sec_veri
  884. verifica:      POP   BX
  885.                CALL  acceso_sector     ; leer para forzar verificación
  886. acc_ret:       PUSHF
  887.                INC   sector            ; preparado para otro sector
  888.                MOV   seccion,0         ; desde su primera sección
  889.                POPF
  890.                POP   AX
  891.                RET
  892.  
  893. acceso_multi:  PUSH  AX                ; AL = sectores a transferir
  894.                MOV   BX,ES             ;      desde 'sector' teniendo
  895.                SHL   BX,4              ;      cuidado con el DMA
  896.                ADD   BX,DI
  897.                NEG   BX                ; BX = bytes hasta frontera DMA
  898.                CALL  num_secciones
  899.                MOV   CH,AL             ; AL secciones de 512 bytes
  900.                MOV   CL,0
  901.                SHL   CX,1              ; CX = bytes por sector
  902.                XCHG  AX,BX             ; BL = secciones por sector
  903.                XOR   DX,DX
  904.                DIV   CX
  905.                MOV   CL,AL             ; CL = sectores que caben
  906.                POP   AX                ; AL = sectores a transferir
  907.                CMP   AL,CL
  908.                JA    acc_mult2         ; no hay problemas con el DMA
  909. acc_mult1:     MOV   CL,AL
  910. acc_mult2:     AND   CL,CL
  911.                JZ    acc_mult3         ; primer sector problemático
  912.                MOV   AH,sector
  913.                MOV   sector_ini,AH
  914.                ADD   AH,CL
  915.                DEC   AH
  916.                MOV   sector_fin,AH
  917.                INC   AH
  918.                SUB   AL,CL
  919.                CALL  acceso_sector     ; sectores no problemáticos
  920.                MOV   sector,AH
  921.                JC    acc_mult_fin
  922. acc_mult3:     AND   AL,AL             ; ahora el sector problemático
  923.                JZ    acc_mult_fin
  924.                ADD   secciones,BL      ; compensar futuro decremento
  925.                CALL  acceso_secc       ; a través del buffer auxiliar
  926.                JC    acc_mult_fin
  927.                DEC   AL
  928.                JMP   acc_mult1         ; sectores que restan
  929. acc_mult_fin:  RET
  930. ejecuta_io     ENDP
  931.  
  932. ; ------------ Mover secciones desde el buffer hacia la memoria (con
  933. ;              orden F_READ) después de la lectura o de la memoria al
  934. ;              buffer (orden F_WRITE) antes de la escritura. En la
  935. ;              verificación (orden F_VERIFY) no se mueve nada porque
  936. ;              esta subrutina no es invocada.
  937.  
  938. trans_secc     PROC
  939.                XPUSH <AX, BX, CX, SI>  ; *
  940.                MOV   BL,seccion        ; desde esta sección
  941.                CALL  num_secciones     ; nº secciones del sector
  942. otra_secci:    PUSH  BX
  943.                SHL   BX,9              ; sección * 512
  944.                ADD   BX,buffer         ; dirección
  945.                MOV   SI,BX
  946.                MOV   CX,256            ; tamaño sección (palabras)
  947.                CALL  swap_reg          ; ¿intercambiar origen-destino?
  948.                REP   MOVSW             ; copiar 512 bytes
  949.                CALL  swap_reg          ; ¿intercambiar origen-destino?
  950.                POP   BX
  951.                DEC   secciones         ; una menos
  952.                JZ    fin_secc
  953.                INC   BX                ; otra sección del sector
  954.                CMP   BL,AL             ; ¿sector agotado?
  955.                JB    otra_secci        ; aún no
  956. fin_secc:      XPOP  <SI, CX, BX, AX>  ; *
  957.                RET
  958. swap_reg:      CMP   CS:orden,F_WRITE
  959.                JE    interc
  960.                CLC
  961.                RET
  962. interc:        XCHG  SI,DI             ; en escritura, invertir el
  963.                XPUSH <ES, DS>          ; sentido de la operación
  964.                XPOP  <ES, DS>
  965.                RET
  966. trans_secc     ENDP
  967.  
  968. ; ------------ Comprobar si el sector ya está en el buffer.
  969.  
  970. leido?         PROC
  971.                PUSH  AX
  972.                MOV   AL,buf_unidad
  973.                CMP   AL,unidad
  974.                JNE   no_leido          ; es en otra unidad
  975.                MOV   AL,cilindro
  976.                MOV   AH,cabezal
  977.                CMP   AX,buf_cilcab
  978.                JNE   no_leido          ; es en otro cilindro/cabezal
  979.                MOV   AL,buf_sector
  980.                CMP   AL,sector
  981.                JNE   no_leido          ; es otro sector
  982.                POP   AX
  983.                RET                     ; está en el buffer
  984. no_leido:      STC
  985.                POP   AX
  986.                RET                     ; sector no leído
  987. leido?         ENDP
  988.  
  989. ; ------------ Leer o escribir sector(es). Se selecciona el tamaño de
  990. ;              sector correcto antes de llamar a sector_io.  En esta
  991. ;              rutina se actualiza la variable «status» en función de
  992. ;              los posibles errores de acceso.  Si sector_fin es
  993. ;              distinto de 0 se accede a los sectores indicados, si es
  994. ;              0 se accede sólo al sector «sector» a través del buffer
  995. ;              intermedio y al final se anota el sector cargado ó
  996. ;              escrito para evitar futuras lecturas innecesarias, a
  997. ;              modo de mini-caché que dispara la velocidad de acceso a
  998. ;              sectores lógicos consecutivos.
  999.  
  1000. acceso_sector  PROC
  1001.                XPUSH <AX, BX>
  1002.                CALL  seek_drv          ; posicionar el cabezal
  1003.                JNC   en_pista
  1004.                CMP   status,0          ; ¿error ya determinado?
  1005.                JNE   acc_fin_err
  1006.                OR    status,40h        ; no: pues 'seek error'
  1007. acc_fin_err:   STC
  1008.                JMP   acceso_fin
  1009. en_pista:      CALL  pista0?
  1010.                MOV   AL,2
  1011.                JZ    tam_acc_ok        ; sectores 512 en cil./cab. 0
  1012.                LEA   BX,[SI].tabla_tsect
  1013.                ADD   BL,sector
  1014.                ADC   BH,0
  1015.                MOV   AL,[BX-1]
  1016. tam_acc_ok:    MOV   tsector,AL
  1017.                CMP   sector_fin,0      ; ¿usar buffer intermedio?
  1018.                JE    acceso_buffer
  1019.                CALL  sector_io
  1020.                MOV   sector_fin,0      ; no acceder a más de 1 sector
  1021.                PUSHF                   ; **1
  1022.                JMP   acceso_rep        ; en el futuro (por defecto)
  1023. acceso_buffer: XPUSH <ES, DI>
  1024.                PUSH  CS
  1025.                POP   ES
  1026.                MOV   DI,buffer         ; acceso con buffer auxiliar
  1027.                MOV   AL,sector         ; mismo sector inicial/final
  1028.                MOV   sector_ini,AL
  1029.                MOV   sector_fin,AL
  1030.                CALL  sector_io
  1031.                MOV   sector_fin,0
  1032.                XPOP  <DI, ES>
  1033.                PUSHF                   ; **2
  1034.                MOV   AL,-1             ; invalidar contenido buffer
  1035.                JC    acceso_anota      ; si hay error
  1036.                CMP   orden,F_VERIFY
  1037.                JE    acceso_rep        ; nada leído físicamente
  1038.                MOV   AL,unidad
  1039. acceso_anota:  MOV   buf_unidad,AL
  1040.                MOV   AL,cilindro
  1041.                MOV   AH,cabezal
  1042.                MOV   buf_cilcab,AX
  1043.                MOV   AL,sector
  1044.                MOV   buf_sector,AL     ; anotado el sector en buffer
  1045. acceso_rep:    POPF                    ; ** mucho cuidado con la pila
  1046.                CALL  set_err           ; ajustar variable «status»
  1047. acceso_fin:    XPOP  <BX, AX>
  1048.                RET
  1049. acceso_sector  ENDP
  1050.  
  1051. ; ------------ Devolver el número de secciones del sector en curso.
  1052.  
  1053. num_secciones  PROC
  1054.                CALL  pista0?
  1055.                MOV   AL,1
  1056.                JZ    num_secc_ok       ; sectores 512 en cil./cab. 0
  1057.                XPUSH <BX, CX>
  1058.                LEA   BX,[SI].tabla_tsect
  1059.                ADD   BL,sector
  1060.                ADC   BH,0
  1061.                MOV   CL,[BX-1]
  1062.                SUB   CL,2
  1063.                MOV   AL,1
  1064.                SHL   AL,CL
  1065.                XPOP  <CX, BX>
  1066. num_secc_ok:   RET                     ; resultado en AL
  1067. num_secciones  ENDP
  1068.  
  1069. ; ------------ Asegurar que el motor está en marcha.
  1070.  
  1071. motor_ok       PROC
  1072.                PUSHA                   ; *
  1073.                PUSH  DS                ; **
  1074.                MOV   BX,40h
  1075.                PUSH  BX
  1076.                POP   DS
  1077.                MOV   CH,255-18         ; CH = 255 - 1 segundo
  1078.                CLI
  1079.                MOV   CL,CS:unidad
  1080.                MOV   AL,1
  1081.                SHL   AL,CL
  1082.                TEST  [BX-1],AL         ; ¿motor en marcha?
  1083.                JZ    arrancarlo        ; arrancarlo
  1084.                CMP   [BX],CH           ; Si encendido y acelerado...
  1085.                JBE   ok_motor          ; ...seguir
  1086. arrancarlo:    OR    [BX-1],AL         ; arrancar motor
  1087.                AND   BYTE PTR [BX-1],0CFh  ; borrar número unidad
  1088.                MOV   AL,CL
  1089.                SHL   AL,4              ; unidad << 4
  1090.                OR    [BX-1],AL         ; nuevo número de unidad
  1091.                MOV   BYTE PTR [BX],255 ; asegurar que no se pare
  1092.                STI
  1093.                MOV   DX,3F2h           ; registro de salida digital
  1094.                ADD   CL,4
  1095.                MOV   AL,1
  1096.                SHL   AL,CL             ; colocar bit del motor
  1097.                OR    AL,CS:unidad      ; seleccionar unidad
  1098.                OR    AL,00001100b      ; modo DMA, no hacer reset
  1099.                OUT   DX,AL             ; poner en marcha el motor
  1100.                MOV   AX,90FDh
  1101.                CLC
  1102.                INT   15h               ; permitir multitarea
  1103.                JC    ok_motor          ; timeout
  1104.                MOV   AX,1000           ; 1 segundo aceleración
  1105.                CALL  retardo           ; esperar aceleración disco
  1106. ok_motor:      MOV   [BX],CH           ; cuenta máxima detención motor
  1107.                STI                     ; sin forzar futura aceleración
  1108.                POP   DS                ; **
  1109.                POPA                    ; *
  1110.                RET
  1111. motor_ok       ENDP
  1112.  
  1113. ; ------------ Establecer modalidad de operación del controlador
  1114. ;              y poner el motor en marcha. Si CF=1 se le da tiempo
  1115. ;              además a la unidad para que acelere.
  1116.  
  1117. reset_drv      PROC
  1118.                PUSHA
  1119.                CALL  motor_off_cnt     ; cuenta detención motor
  1120.                MOV   CL,unidad
  1121.                MOV   AL,CL
  1122.                SHL   AL,2              ; unidad seleccionada
  1123.                OR    AL,1              ; bit de motor
  1124.                SHL   AL,CL             ; colocar dicho bit
  1125.                PUSH  DS                ; *
  1126.                PUSH  40h
  1127.                POP   DS
  1128.                CLI
  1129.                MOV   DS:[3Fh],AL
  1130.                AND   BYTE PTR DS:[3Eh],70h ; bit IRQ=0 y recalibrar
  1131.                POP   DS                ; *
  1132.                SHL   AL,4              ; bits motor en nibble alto
  1133.                OR    AL,CL             ; seleccionar unidad
  1134.                OR    AL,00001000b      ; interrupciones+DMA y reset
  1135.                MOV   DX,3F2h           ; registro de salida digital
  1136.                OUT   DX,AL             ; señal de reset
  1137.                CALL  fdc_respiro       ; tiempo reconocer reset en 486
  1138.                OR    AL,00000100b
  1139.                OUT   DX,AL             ; fin de señal de reset
  1140.                CALL  espera_int        ; rehabilitará interrupciones
  1141.                MOV   AL,8
  1142.                CALL  fdc_write         ; comando 'leer estado int...'
  1143.                CALL  fdc_read
  1144.                CALL  fdc_read
  1145.                CALL  envia_specify     ; comando 'specify' adecuado
  1146.                POPA
  1147.                RET
  1148. reset_drv      ENDP
  1149.  
  1150. ; ------------ Enviar comando specify a la controladora. El step-rate
  1151. ;              se selecciona según la densidad, para evitar un sonido
  1152. ;              extraño al posicionar o recalibrar el cabezal.
  1153.  
  1154. envia_specify  PROC
  1155.                PUSH  AX
  1156.                PUSH  DS
  1157.                PUSH  40h
  1158.                POP   DS
  1159.                MOV   AH,DS:[8Bh]
  1160.                POP   DS
  1161.                MOV   AL,3              ; comando 'specify'
  1162.                CALL  fdc_write
  1163.                MOV   AL,0BFh           ; step rate para 500 kbps
  1164.                AND   AH,11000000b
  1165.                JZ    spec1_ok
  1166.                MOV   AL,0AFh           ; step rate para 1 Mbps
  1167.                CMP   AH,11000000b
  1168.                JE    spec1_ok
  1169.                MOV   AL,0DFh           ; step rate para 250/300 Kbps
  1170. spec1_ok:      CALL  fdc_write
  1171.                MOV   AL,2
  1172.                CALL  fdc_write         ; head load y modo DMA
  1173.                POP   AX
  1174.                RET
  1175. envia_specify  ENDP
  1176.  
  1177. ; ------------ Recargar cuenta para la detención del motor. Si CF=1 al
  1178. ;              entrar, se establece la mayor cuenta posible; en caso
  1179. ;              contrario, se pone el valor normal de la tabla base.
  1180.  
  1181. motor_off_cnt  PROC
  1182.                PUSHA
  1183.                PUSH  DS
  1184.                MOV   AL,0FFh           ; valor máximo
  1185.                JC    motor_off_ok
  1186.                PUSH  0
  1187.                POP   DS
  1188.                LDS   BX,DWORD PTR DS:[1Eh*4] ; DS:BX -> INT 1Eh
  1189.                MOV   AL,[BX+2]               ; byte 2 tabla base disco
  1190. motor_off_ok:  PUSH  40h
  1191.                POP   DS
  1192.                MOV   BYTE PTR DS:[40h],AL  ; cuenta parada motor
  1193.                POP   DS
  1194.                POPA
  1195.                RET
  1196. motor_off_cnt  ENDP
  1197.  
  1198. ; ------------ Llevar el cabezal a la pista indicada, recalibrando si
  1199. ;              hubo un reset (se invocó la función 0 de la INT 13h o
  1200. ;              se ejecutó reset_drv) antes de esta operación. Primero
  1201. ;              se selecciona la velocidad de transferencia y se borra
  1202. ;              el resultado de cualquier operación anterior, para que
  1203. ;              todo quede listo para el próximo acceso a disco.
  1204.  
  1205. seek_drv       PROC
  1206.                PUSHA
  1207.                CALL  set_rate          ; velocidad / borrar resultados
  1208.                CALL  envia_specify     ; comando 'specify' adecuado
  1209.                MOV   AH,1
  1210.                MOV   CL,unidad
  1211.                SHL   AH,CL             ; AH = 1 (A:) ó 2 (B:)
  1212.                PUSH  DS
  1213.                PUSH  40h
  1214.                POP   DS
  1215.                TEST  AH,DS:[3Eh]
  1216.                POP   DS
  1217.                JNZ   do_seek           ; la unidad ya fue recalibrada
  1218.                CALL  recalibrar
  1219.                JC    fallo_seek        ; fallo al recalibrar
  1220. do_seek:       MOV   BX,94h
  1221.                ADD   BL,unidad
  1222.                MOV   AL,cilindro
  1223.                PUSH  DS                ; *
  1224.                PUSH  40h
  1225.                POP   DS
  1226.                OR    DS:[3Eh],AH       ; unidad ya recalibrada
  1227.                MOV   AH,DS:[41h]       ; código de error previo
  1228.                CMP   AL,[BX]
  1229.                MOV   [BX],AL
  1230.                POP   DS                ; *
  1231.                JNE   hacer_seek        ; seek necesario
  1232.                CMP   AH,40h            ; ¿error de seek previo?
  1233.                JNE   seek_ok           ; no, evitar seek innecesario
  1234. hacer_seek:    MOV   AL,0Fh
  1235.                CALL  fdc_write         ; comando 'seek'
  1236.                JC    fallo_seek
  1237.                MOV   AL,cabezal
  1238.                SHL   AL,2
  1239.                OR    AL,unidad
  1240.                CALL  fdc_write         ; enviar HD, US1, US0
  1241.                MOV   AL,cilindro
  1242.                CALL  fdc_write         ; enviar cilindro
  1243.                CALL  espera_int        ; esperar interrupción
  1244.                JC    fallo_seek
  1245.                MOV   AL,8
  1246.                CALL  fdc_write         ; comando 'leer estado int...'
  1247.                JC    fallo_seek
  1248.                CALL  fdc_read          ; leer registro de estado 0
  1249.                JC    fallo_seek
  1250.                MOV   AH,AL
  1251.                CALL  fdc_read          ; leer cilindro actual
  1252.                TEST  AH,11000000b      ; comprobar ST0
  1253.                JNZ   fallo_seek
  1254.                MOV   AL,15             ; estabilización para escritura
  1255.                CMP   orden,F_WRITE
  1256.                JE    rseek_ok
  1257.                MOV   AL,1              ; estabilización para lectura
  1258. rseek_ok:      CBW                     ; AH = 0
  1259.                CALL  retardo           ; esperar asentamiento cabezal
  1260. seek_ok:       POPA
  1261.                CLC                     ; retornar con éxito
  1262.                RET
  1263. fallo_seek:    POPA
  1264.                STC                     ; retornar indicando fallo
  1265.                RET
  1266. seek_drv       ENDP
  1267.  
  1268. ; ------------ Establecer velocidad de transferencia correcta si aún
  1269. ;              no ha sido seleccionada y borrar el resultado de otra
  1270. ;              operación previa.
  1271.  
  1272. set_rate       PROC
  1273.                PUSHA
  1274.                CALL  pista0?
  1275.                MOV   AX,[SI].vunidad   ; velocidad pista 0 / demás
  1276.                JZ    vel_ok
  1277.                MOV   AL,AH
  1278. vel_ok:        PUSH  DS                ; *
  1279.                PUSH  40h
  1280.                POP   DS
  1281.                MOV   AH,DS:[8Bh]
  1282.                SHR   AH,6              ; aislar bits de velocidad
  1283.                CMP   AL,AH
  1284.                JE    vel_set           ; velocidad ya seleccionada
  1285.                MOV   DX,3F7h
  1286.                OUT   DX,AL             ; seleccionarla
  1287.                SHL   AL,6
  1288.                AND   BYTE PTR DS:[8Bh],00111111b
  1289.                OR    DS:[8Bh],AL
  1290. vel_set:       POP   DS                ; *
  1291.                LEA   DI,status
  1292.                MOV   CX,8
  1293. borra_status:  MOV   [DI],CH           ; borrar información de estado
  1294.                INC   DI
  1295.                LOOP  borra_status
  1296.                POPA
  1297.                RET
  1298. set_rate       ENDP
  1299.  
  1300. ; ------------ Recalibrar la unidad (si hay error se intenta otra vez
  1301. ;              para el caso de que deba moverse más de 77 pistas).
  1302.  
  1303. recalibrar     PROC
  1304.                PUSHA
  1305.                MOV   BX,94h
  1306.                ADD   BL,unidad
  1307.                PUSH  DS                ; *
  1308.                PUSH  40h
  1309.                POP   DS
  1310.                MOV   [BX],BH           ; pista actual = 0
  1311.                POP   DS                ; *
  1312.                MOV   CX,2              ; dos veces como mucho
  1313. recalibra:     MOV   AL,7
  1314.                CALL  fdc_write         ; comando de 'recalibrado'
  1315.                JC    fallo_recal
  1316.                MOV   AL,cabezal
  1317.                SHL   AL,2
  1318.                OR    AL,unidad
  1319.                CALL  fdc_write         ; enviar HD, US1, US0
  1320.                JC    fallo_recal
  1321.                CALL  espera_int        ; esperar interrupción
  1322.                JC    fallo_recal
  1323.                MOV   AL,8
  1324.                CALL  fdc_write         ; comando 'leer estado int...'
  1325.                JC    fallo_recal
  1326.                CALL  fdc_read          ; leer registro de estado 0
  1327.                JC    fallo_recal
  1328.                MOV   AH,AL
  1329.                CALL  fdc_read          ; leer cilindro actual
  1330.                XOR   AH,00100000b      ; bajar bit de 'seek end'
  1331.                TEST  AH,11110000b      ; comprobar resultado y ST0
  1332.                JNZ   fallo_recal       ; sin 'seek end' o TRK0
  1333.                MOV   AX,1              ; pausa de 1 ms
  1334.                CALL  retardo
  1335.                JMP   recal_ret
  1336. fallo_recal:   LOOP  recalibra         ; reintentar comando
  1337.                STC                     ; condición de fallo
  1338. recal_ret:     POPA
  1339.                RET
  1340. recalibrar     ENDP
  1341.  
  1342. ; ------------ Cargar o escribir sector(es) del disco en ES:DI,
  1343. ;              actualizando la dirección en ES:DI pero sin alterar
  1344. ;              ningún otro registro. Si hay error se devuelve CF=1 y
  1345. ;              no se modifica ES:DI. A partir de fdc_result se dejan
  1346. ;              los 7 bytes que devuelve el FDC al final del acceso.
  1347. ;              En caso de verificación (F_VERIFY) se programa el DMA
  1348. ;              para que no realice transferencia física (convenio de
  1349. ;              las BIOS con fecha 15/11/85 y posterior).
  1350.  
  1351. sector_io      PROC
  1352.                XPUSH <AX, BX, CX, DX>
  1353.                MOV   CL,tsector
  1354.                MOV   CH,0
  1355.                STC
  1356.                RCL   CH,CL
  1357.                MOV   CL,0              ; nº de bytes por sector
  1358.                MOV   AL,sector_fin
  1359.                SUB   AL,sector_ini
  1360.                INC   AX
  1361.                CBW                     ; AX sectores (AH = 0)
  1362.                MUL   CX
  1363.                MOV   DX,AX             ; bytes totales
  1364.                MOV   CX,AX
  1365.                DEC   CX                ; bytes totales - 1
  1366.                MOV   AX,ES
  1367.                CALL  calc_dir_DMA      ; AX:DI -> base BX y página AH
  1368.                MOV   AL,orden          ; modo DMA necesario
  1369.                CALL  prepara_DMA
  1370.                CMP   AL,F_WRITE
  1371.                MOV   AL,11000101b      ; comando de escritura del FDC
  1372.                JE    orden_io_ok
  1373.                MOV   AL,11100110b      ; comando leer (verif.) del FDC
  1374. orden_io_ok:   CALL  fdc_write         ; comando leer/escribir del FDC
  1375.                JC    sector_io_ko
  1376.                MOV   AL,cabezal
  1377.                SHL   AL,2
  1378.                OR    AL,unidad
  1379.                CALL  fdc_write         ; byte 1 de la orden
  1380.                MOV   AL,cilindro
  1381.                CALL  fdc_write         ; enviar cilindro
  1382.                MOV   AL,cabezal
  1383.                CALL  fdc_write         ; enviar cabezal
  1384.                MOV   AL,sector_ini
  1385.                CALL  fdc_write         ; enviar nº sector
  1386.                MOV   AL,tsector
  1387.                CALL  fdc_write         ; longitud sector
  1388.                MOV   AL,sector_fin
  1389.                CALL  fdc_write         ; último sector
  1390.                MOV   AL,[SI].gap
  1391.                CALL  fdc_write         ; GAP de lectura/escritura
  1392.                MOV   AL,128
  1393.                CALL  fdc_write         ; tamaño sector si longitud=0
  1394.                CALL  espera_int
  1395.                PUSHF                   ; *
  1396.                LEA   BX,fdc_result
  1397.                MOV   CX,7
  1398. sect_io_res:   CALL  fdc_read          ; leyendo resultados
  1399.                MOV   [BX],AL
  1400.                INC   BX
  1401.                LOOP  sect_io_res
  1402.                POPF                    ; *
  1403.                JC    sector_io_ko
  1404.                TEST  fdc_result,11000000b
  1405.                JNZ   sector_io_ko
  1406.                ADD   DI,DX             ; actualizar dirección
  1407.                CLC                     ; Ok
  1408.                JMP   sector_io_fin
  1409. sector_io_ko:  STC                     ; indicar fallo
  1410. sector_io_fin: XPOP  <DX, CX, BX, AX>
  1411.                RET
  1412. sector_io      ENDP
  1413.  
  1414. ; ------------ Devolver en AH la página de DMA y en BX la base. A la
  1415. ;              entrada, AX:DI -> dirección de memoria y CX = bytes-1.
  1416. ;              Se supone que el buffer no cruza una frontera de DMA.
  1417.  
  1418. calc_dir_DMA   PROC
  1419.                PUSH  DX
  1420.                MOV   BX,16
  1421.                MUL   BX
  1422.                ADD   AX,DI
  1423.                ADC   DX,0              ; DX:AX = dirección 20 bits
  1424.                MOV   BX,AX             ; base en BX
  1425.                MOV   AH,DL             ; página
  1426.                POP   DX
  1427.                RET
  1428. calc_dir_DMA   ENDP
  1429.  
  1430. ; ------------ Crear tabla con información para formatear. En ES:BX
  1431. ;              está el futuro sector de arranque del disquete.
  1432.  
  1433. genera_info    PROC
  1434.                PUSHA
  1435.                MOV   buf_unidad,-1     ; invalidar contenido buffer
  1436.                MOV   SI,buffer
  1437.                MOV   DI,BX
  1438.                CALL  pista0?
  1439.                JNZ   no_cilcab0             ; no es cilindro/cabezal 0
  1440.                ADD   DI,ES:[BX+70]          ; DI -> datos pista 0
  1441.                MOV   CL,ES:[DI]
  1442.                MOV   CH,0                   ; CX sectores en pista 0
  1443.                INC   DI
  1444.                MOV   AL,ES:[DI]             ; GAP para pista 0
  1445.                MOV   AH,0                   ; byte de relleno
  1446.                INC   DI
  1447.                MOV   BYTE PTR [SI],2        ; tamaño de sector
  1448.                MOV   BYTE PTR [SI+1],CL     ; número de sectores
  1449.                MOV   [SI+2],AX              ; GAP / byte de relleno
  1450. genera_0:      ADD   SI,4
  1451.                MOV   AL,cilindro
  1452.                MOV   AH,cabezal
  1453.                MOV   [SI],AX                ; datos para cada sector
  1454.                MOV   AL,ES:[DI]
  1455.                MOV   AH,2                   ; LOG2 (tamaño)-7
  1456.                INC   DI
  1457.                MOV   [SI+2],AX              ; nº de sector / tamaño
  1458.                LOOP  genera_0
  1459.                POPA
  1460.                RET
  1461. no_cilcab0:    ADD   DI,ES:[BX+72]
  1462.                CMP   BYTE PTR ES:[BX+65],1
  1463.                JE    info_stv
  1464.                MOV   DL,ES:[DI+2]           ; tamaño /F
  1465.                MOV   DH,ES:[DI]             ; nº sectores
  1466.                MOV   [SI],DX
  1467.                XCHG  DH,DL                  ; tamaño en DH
  1468.                MOV   CL,DL
  1469.                MOV   CH,0                   ; CX sectores
  1470.                MOV   AL,ES:[DI+1]           ; GAP para formatear
  1471.                MOV   AH,0                   ; byte relleno /F
  1472.                MOV   [SI+2],AX              ; GAP / byte de relleno
  1473.                PUSH  DX
  1474.                MOV   AX,ES:[DI+3]
  1475.                PUSH  AX
  1476.                ADD   AL,AH
  1477.                MUL   cilindro
  1478.                MOV   DX,AX
  1479.                POP   AX
  1480.                MUL   cabezal
  1481.                ADD   AX,DX
  1482.                XOR   DX,DX
  1483.                MOV   BL,ES:[DI]
  1484.                MOV   BH,0
  1485.                DIV   BX                ; DL = módulo
  1486.                SUB   DL,ES:[DI]
  1487.                NEG   DL
  1488.                MOV   AL,DL
  1489.                POP   DX                ; restaurar tamaño en DH
  1490.                MOV   DL,AL             ; primer sector de la pista - 1
  1491.                MOV   BL,ES:[DI]        ; nº sectores en la pista
  1492. genera_pn:     ADD   SI,4
  1493.                INC   DX
  1494.                CMP   DL,BL
  1495.                JBE   ns_ok
  1496.                MOV   DL,1              ; empezar desde el 1
  1497. ns_ok:         MOV   AL,cilindro
  1498.                MOV   AH,cabezal
  1499.                MOV   [SI],AX           ; datos para cada sector
  1500.                MOV   [SI+2],DX         ; nº sector / LOG2 (tamaño)-7
  1501.                LOOP  genera_pn
  1502.                POPA
  1503.                RET
  1504. info_stv:      MOV   CH,ES:[DI]             ; nº sectores
  1505.                MOV   CL,0                   ; CL:CH sectores
  1506.                MOV   [SI],CX                ; tamaño (CL=0) y número
  1507.                XCHG  CH,CL                  ; CX sectores
  1508.                MOV   AL,ES:[DI+1]           ; GAP para formatear
  1509.                MOV   AH,4Eh                 ; byte de relleno /M
  1510.                MOV   [SI+2],AX              ; GAP / byte de relleno
  1511.                MOV   DL,128
  1512. genera_otro:   ADD   SI,4
  1513.                INC   DX
  1514.                MOV   AL,cilindro
  1515.                MOV   AH,cabezal
  1516.                MOV   [SI],AX           ; datos para cada sector
  1517.                XPUSH <CX, DI>          ; *
  1518.                MOV   CL,ES:[DI+2]      ; CH está a 0
  1519. busca_num:     ADD   DI,3
  1520.                CMP   DL,ES:[DI]
  1521.                MOV   AX,ES:[DI+1]      ; número de sector / tamaño
  1522.                JE    hallado           ; es sector a cambiar número
  1523.                LOOP  busca_num
  1524.                MOV   AL,DL             ; no cambiar número
  1525.                MOV   AH,0              ; e indicar tamaño 128
  1526. hallado:       XPOP  <DI, CX>          ; *
  1527.                MOV   [SI+2],AX         ; nº sector / LOG2 (tamaño)-7
  1528.                LOOP  genera_otro
  1529.                POPA
  1530.                RET
  1531. genera_info    ENDP
  1532.  
  1533. ; ------------ Formatear una pista.
  1534.  
  1535. formatea_pista PROC
  1536.                PUSHA
  1537.                MOV   BX,buffer
  1538.                MOV   DI,BX
  1539.                MOV   CL,[BX+1]
  1540.                MOV   CH,0              ; CX sectores
  1541.                SHL   CX,2
  1542.                DEC   CX                ; nº de bytes - 1
  1543.                MOV   AX,DS
  1544.                CALL  calc_dir_DMA      ; AX:DI -> base BX y página AH
  1545.                MOV   AL,4Ah            ; modo DMA para escribir
  1546.                ADD   BX,4              ; saltar primeros 4 bytes
  1547.                CALL  prepara_DMA
  1548.                MOV   BX,buffer
  1549.                MOV   AL,F_FORMAT
  1550.                CALL  fdc_write
  1551.                JC    fallo_fmt
  1552.                MOV   AL,cabezal
  1553.                SHL   AL,2
  1554.                OR    AL,unidad
  1555.                CALL  fdc_write         ; byte 1 de la orden
  1556.                JC    fallo_fmt
  1557.                MOV   CX,4
  1558. format_cmd:    MOV   AL,[BX]
  1559.                CALL  fdc_write
  1560.                INC   BX
  1561.                LOOP  format_cmd
  1562.                CALL  espera_int
  1563. fallo_fmt:     PUSHF
  1564.                LEA   BX,fdc_result
  1565.                MOV   CX,7
  1566. format_res:    CALL  fdc_read          ; leyendo resultados
  1567.                MOV   [BX],AL
  1568.                INC   BX
  1569.                LOOP  format_res
  1570.                POPF
  1571.                JC    fallo_format
  1572.                TEST  fdc_result,11000000b
  1573.                JZ    format_ret
  1574. fallo_format:  STC                     ; fallo
  1575. format_ret:    POPA
  1576.                RET
  1577. formatea_pista ENDP
  1578.  
  1579. ; ------------ Esperar interrupción de disquete durante casi 2
  1580. ;              segundos antes de considerar que ha sido un fracaso.
  1581.  
  1582. espera_int     PROC
  1583.                STI
  1584.                PUSHA
  1585.                XPUSH <DS, 40h>
  1586.                POP   DS
  1587.                MOV   AX,9001h
  1588.                CLC
  1589.                INT   15h               ; permitir multitarea
  1590.                MOV   DX,0280h
  1591.                MOV   BX,3Eh
  1592.                JC    timeout_int
  1593. esp_int_1s:    XOR   CX,CX
  1594. esp_int:       TEST  [BX],DL           ; ¿llegó la interrupción?
  1595.                JNZ   fin_espera
  1596.                PMICRO
  1597.                LOOP  esp_int           ; esperar durante casi 1 seg.
  1598.                DEC   DH
  1599.                JNZ   esp_int_1s
  1600. timeout_int:   OR    CS:status,DL      ; timeout
  1601.                STC
  1602. fin_espera:    PUSHF
  1603.                AND   BYTE PTR [BX],7Fh ; para la próxima vez
  1604.                POPF
  1605.                POP   DS
  1606.                POPA
  1607.                RET
  1608. espera_int     ENDP
  1609.  
  1610. ; ------------ Preparar DMA para E/S. A la entrada, BX = dirección de
  1611. ;              base, AH = registro de página y CX = nº bytes - 1.
  1612.  
  1613. prepara_DMA    PROC
  1614.                PUSH  AX
  1615.                CLI
  1616.                OUT   0Bh,AL            ; registro de modo del DMA
  1617.                MOV   AL,0
  1618.                DELAY
  1619.                OUT   0Ch,AL            ; clear first/last flip-flop
  1620.                MOV   AL,BL
  1621.                DELAY
  1622.                OUT   4,AL
  1623.                MOV   AL,BH
  1624.                DELAY
  1625.                OUT   4,AL              ; enviada dirección base
  1626.                DELAY
  1627.                MOV   AL,AH
  1628.                OUT   81h,AL            ; registro de página del DMA
  1629.                MOV   AL,CL
  1630.                DELAY
  1631.                OUT   5,AL
  1632.                MOV   AL,CH
  1633.                DELAY
  1634.                OUT   5,AL              ; enviada cuenta de bytes
  1635.                STI
  1636.                MOV   AL,2
  1637.                DELAY
  1638.                OUT   0Ah,AL            ; habilitar canal 2 de DMA
  1639.                POP   AX
  1640.                RET
  1641. prepara_DMA    ENDP
  1642.  
  1643. ; ------------ Recibir byte del FDC en AL. A la vuelta, CF=1 si
  1644. ;              la operación fracasó (el FDC no estaba listo) y
  1645. ;              se indica la condición de timeout en «status».
  1646.  
  1647. fdc_read       PROC
  1648.                XPUSH <CX, DX, AX>
  1649.                CALL  fdc_respiro       ; no abrasar el FDC
  1650.                MOV   DX,3F4h           ; registro de estado del FDC
  1651.                MOV   CX,133            ; constante para 0,002 segundos
  1652. espera_rd:     DELAY
  1653.                IN    AL,DX
  1654.                AND   AL,11000000b
  1655.                CMP   AL,11000000b      ; ¿dato listo?
  1656.                JE    fdc_rd_ok
  1657.                DELAY
  1658.                IN    AL,61h
  1659.                AND   AL,10h
  1660.                CMP   AL,AH
  1661.                JE    espera_rd         ; reintentarlo durante 15,09 µs
  1662.                MOV   AH,AL
  1663.                LOOP  espera_rd
  1664.                XPOP  <AX, DX, CX>
  1665.                OR    status,80h        ; timeout
  1666.                MOV   AL,0
  1667.                STC                     ; fallo
  1668.                RET
  1669. fdc_rd_ok:     POP   AX
  1670.                INC   DX                ; apuntar al registro de datos
  1671.                DELAY
  1672.                IN    AL,DX             ; leer byte del FDC
  1673.                XPOP  <DX, CX>
  1674.                CLC                     ; Ok
  1675.                RET
  1676. fdc_read       ENDP
  1677.  
  1678. ; ------------ Enviar byte AL al FDC. A la vuelta, CF=1 si
  1679. ;              la operación fracasó (el FDC no estaba listo) y
  1680. ;              se indica la condición de timeout en «status».
  1681.  
  1682. fdc_write      PROC
  1683.                XPUSH <CX, DX, AX>
  1684.                CALL  fdc_respiro       ; no abrasar el FDC
  1685.                MOV   DX,3F4h           ; registro de estado del FDC
  1686.                MOV   CX,133            ; constante para 0,002 segundos
  1687. espera_wr:     DELAY
  1688.                IN    AL,DX
  1689.                TEST  AL,80h            ; ¿listo para E/S?
  1690.                JNZ   fdc_wr_ok
  1691.                DELAY
  1692.                IN    AL,61h
  1693.                AND   AL,10h
  1694.                CMP   AL,AH
  1695.                JE    espera_wr         ; reintentarlo durante 15,09 µs
  1696.                MOV   AH,AL
  1697.                LOOP  espera_wr
  1698.                XPOP  <AX, DX, CX>
  1699.                OR    status,80h        ; timeout
  1700.                STC                     ; fallo
  1701.                RET
  1702. fdc_wr_ok:     INC   DX                ; apuntar al registro de datos
  1703.                POP   AX
  1704.                DELAY
  1705.                OUT   DX,AL             ; enviar byte al FDC
  1706.                XPOP  <DX, CX>
  1707.                CLC                     ; Ok
  1708.                RET
  1709. fdc_write      ENDP
  1710.  
  1711. ; ------------ Retardo de 60 µs para dar tiempo al FDC en 486 rápidos.
  1712.  
  1713. fdc_respiro    PROC
  1714.                XPUSH <AX, CX>
  1715.                MOV   CX,4
  1716. fdc_ret:       PMICRO
  1717.                LOOP  fdc_ret
  1718.                XPOP  <CX, AX>
  1719.                RET
  1720. fdc_respiro    ENDP
  1721.  
  1722. ; ------------ Esperar exactamente AX milisegundos.
  1723.  
  1724. retardo        PROC
  1725.                PUSHF
  1726.                PUSHA
  1727.                MOV   DX,16970          ; 16970 = 1193180/18*256/1000
  1728.                MUL   DX
  1729.                MOV   CL,AH             ; dividir DX:AX entre 256 y
  1730.                MOV   CH,DL             ; dejar el resultado en DX:CX
  1731.                MOV   DL,DH
  1732.                MOV   DH,0              ; DX:CX 15,09 µs-avos
  1733. retardando:    PMICRO
  1734.                LOOP  retardando
  1735.                AND   DX,DX
  1736.                JZ    retardado
  1737.                DEC   DX
  1738.                JMP   retardando
  1739. retardado:     POPA
  1740.                POPF
  1741.                RET
  1742. retardo        ENDP
  1743.  
  1744.                ; --- Ubicación del sector de hasta 2048 bytes.
  1745.  
  1746.                EVEN
  1747. buffer_io      EQU   $
  1748. tbuffer        EQU   2048
  1749.  
  1750. fin_residente  EQU   $  ; fin del área residente sin contar el buffer
  1751.  
  1752. bytes_resid    EQU   fin_residente-ini_residente
  1753.  
  1754.  
  1755. ; *****************************
  1756. ; *                           *
  1757. ; *   I N S T A L A C I O N   *
  1758. ; *                           *
  1759. ; *****************************
  1760.  
  1761. main           PROC
  1762.                CALL  obtener_param     ; analizar posibles parámetros
  1763.                JNC   params_ok         ; son correctos
  1764.                OR    error,MALSINTAX
  1765.                JMP   informar
  1766. params_ok:     CALL  inic_general      ; inicializar ciertas variables
  1767.                CALL  pc_ok?
  1768.                TEST  error,MALPC or MALDOS or MALBIOS or MALDRV
  1769.                JZ    proc_tsr
  1770.                JMP   informar          ; no hay configuración correcta
  1771. proc_tsr:      CMP   param_u,ON        ; ¿se solicita desinstalarlo?
  1772.                JNE   cont_ins          ; no
  1773.                CALL  residente?        ; sí: ¿está residente?
  1774.                JNC   desinstalable
  1775.                OR    error,NOINSTALADO ; programa aún no instalado
  1776.                JMP   informar
  1777. desinstalable: CALL  testWin
  1778.                JNC   win_out_ok
  1779.                OR    error,WIN_UNLOAD
  1780.                JMP   informar          ; no desinstalar desde Windows
  1781. win_out_ok:    OR    accion,DESINSTALADO
  1782.                MOV   ES,tsr_seg
  1783.                MOV   DI,tsr_off
  1784.                TEST  BYTE PTR ES:[DI-10],2
  1785.                JZ    no_sys
  1786.                OR    error,INSTALADOSYS  ; no desinstalar versión SYS
  1787.                JMP   informar
  1788. no_sys:        MOV   AH,ES:[DI-9]
  1789.                CALL  mx_unload         ; desinstalarlo:
  1790.                JNC   informar          ; ha sido posible
  1791.                OR    error,IMPDESINS   ; no es posible
  1792.                JMP   informar
  1793. cont_ins:      CALL  residente?
  1794.                JNC   ya_reside
  1795.                CMP   AX,0              ; ¿reside una versión distinta?
  1796.                JE    instalable
  1797.                OR    error,NOINSTALABLE  ; versión incompatible
  1798.                JMP   informar
  1799. ya_reside:     OR    accion,YAINSTALADO
  1800.                JMP   informar
  1801. instalable:    CALL  testWin
  1802.                JNC   win_in_ok
  1803.                OR    error,WIN_LOAD
  1804.                JMP   informar          ; no instalar desde Windows
  1805. win_in_ok:     OR    accion,INSTALADO
  1806.                CALL  instalar_tsr      ; instalar el TSR
  1807.                MOV   ES,tsr_seg
  1808. informar:      CALL  info
  1809.                MOV   DX,mem640         ; tamaño zona residente
  1810.                AND   DX,DX
  1811.                JZ    fin_noresid
  1812.                MOV   AX,3100h
  1813.                INT   21h               ; terminar residente
  1814. fin_noresid:   MOV   AX,4C00h
  1815.                INT   21h               ; terminar no residente
  1816. main           ENDP
  1817.  
  1818. instalar_tsr   PROC
  1819.                CALL  mx_get_handle     ; obtener entrada Multiplex
  1820.                JNC   handle_ok
  1821.                OR    error,MX64FULL    ; no quedan entradas
  1822.                RET
  1823. handle_ok:     MOV   multiplex_id,AH   ; entrada multiplex para 2M
  1824.                CALL  preservar_ints    ; tomar nota de vectores
  1825.                CMP   param_ml,ON       ; ¿se indicó parámetro /ML?
  1826.                JE    instalar_ml       ; en efecto
  1827.                MOV   AX,parrafos_resid ; área residente sin PSP
  1828.                MOV   mem640,AX         ; copia de seguridad :)
  1829.                CALL  UMB_alloc         ; pedir memoria superior XMS
  1830.                JC    busca_upper       ; no hay la suficiente
  1831.                LEA   BX,buffer_io-256  ; AX:BX dirección del buffer
  1832.                CALL  testDMA
  1833.                PUSH  AX
  1834.                MOV   DX,AX
  1835.                MOV   AH,12h
  1836.                CALL  gestor_XMS        ; redimensionar bloque memoria
  1837.                CMP   AX,1
  1838.                JE    xms_redim_ok
  1839.                MOV   AX,mem640
  1840.                MOV   parrafos_resid,AX ; función no soportada
  1841. xms_redim_ok:  POP   AX
  1842.                CLC                     ; indicar que usa memoria XMS
  1843.                JMP   instalar_umb
  1844. busca_upper:   MOV   AX,parrafos_resid
  1845.                CALL  UPPER_alloc       ; pedir memoria superior DOS 5
  1846.                JC    instalar_ml       ; no hay la suficiente
  1847.                LEA   BX,buffer_io-256  ; AX:BX dirección del buffer
  1848.                CALL  testDMA
  1849.                XPUSH <AX, ES>
  1850.                MOV   ES,AX
  1851.                MOV   AH,4Ah
  1852.                INT   21h               ; redimensionar bloque memoria
  1853.                CALL  upper_fork        ; dejar residente el bloque
  1854.                XPOP  <ES, AX>
  1855.                STC                     ; indicar que usa memoria DOS
  1856. instalar_umb:  MOV   ES,AX             ; segmento del bloque UMB
  1857.                MOV   DI,0              ; ES:0 zona a donde reubicar
  1858.                MOV   mem640,DI         ; no terminar residente
  1859.                JMP   instalar_asi
  1860. instalar_ml:   ADD   parrafos_resid,6  ; respetar 96 bytes de PSP
  1861.                MOV   AX,CS
  1862.                LEA   BX,buffer_io-(256-96)
  1863.                CALL  testDMA
  1864.                MOV   mem640,BX         ; ocupar memoria convencional
  1865.                STC
  1866.                MOV   DI,96             ; instalación mem. convencional
  1867.                CALL  free_environ      ; liberar espacio de entorno
  1868. instalar_asi:  CALL  inicializa_id     ; inicializar identificación
  1869.                CALL  reubicar_prog     ; reubicar programa a ES:DI
  1870.                CALL  activar_ints      ; interceptar vectores
  1871.                RET
  1872. instalar_tsr   ENDP
  1873.  
  1874. ;*********************************************************
  1875. ;*                                                       *
  1876. ;*  SUBRUTINAS DE PROPOSITO GENERAL PARA LA INSTALACION  *
  1877. ;*                                                       *
  1878. ;*********************************************************
  1879.  
  1880. ; ------------ Extraer posibles parámetros de la línea de comandos.
  1881.  
  1882. obtener_param  PROC
  1883.                CALL  param_mays        ; mayusculizar parámetros
  1884.                MOV   BX,81h            ; apuntar a zona de parámetros
  1885. otro_pmt_mas:  CALL  saltar_esp        ; saltar delimitadores
  1886.                JNC   otro_pmt          ; quedan más parámetros
  1887.                CMP   param_ayuda,ON
  1888.                JE    mal_proc_pmt      ; 'error' de ayuda
  1889.                CLC                     ; parámetros bien procesados
  1890.                RET
  1891. otro_pmt:      CMP   AL,'/'
  1892.                JE    pmt_barrado       ; parámetro precedido por '/'
  1893.                CMP   AL,'?'
  1894.                JNE   mal_proc_pmt
  1895. pmt_hlp:       MOV   param_ayuda,ON
  1896.                INC   BX
  1897.                JMP   otro_pmt_mas
  1898. pmt_barrado:   INC   BX
  1899.                MOV   AL,[BX]           ; letra del parámetro
  1900.                CMP   AL,13             ; ¿fin de mandatos?
  1901.                JE    mal_proc_pmt      ; falta parámetro
  1902.                CMP   AL,'?'
  1903.                JE    pmt_hlp
  1904.                CMP   AL,'H'
  1905.                JE    pmt_hlp
  1906.                CMP   AL,'U'
  1907.                JE    pmt_U             ; parámetro /U
  1908.                CMP   AL,'I'
  1909.                JE    pmt_I             ; parámetro /I
  1910.                CMP   AL,'W'
  1911.                JE    pmt_W             ; parámetro /W
  1912.                MOV   SI,[BX]           ; ¿parámetro de dos caracteres?
  1913.                CMP   SI,"LM"
  1914.                JE    pmt_ML            ; parámetro /ML
  1915. mal_proc_pmt:  STC                     ; error en parámetro(s)
  1916.                RET
  1917. pmt_U:         MOV   param_u,ON
  1918.                INC   BX
  1919.                JMP   otro_pmt_mas
  1920. pmt_I:         MOV   param_i,ON
  1921.                INC   BX
  1922.                JMP   otro_pmt_mas
  1923. pmt_W:         MOV   param_w,ON
  1924.                INC   BX
  1925.                JMP   otro_pmt_mas
  1926. pmt_ML:        MOV   param_ml,ON
  1927.                ADD   BX,2
  1928.                JMP   otro_pmt_mas
  1929. obtener_param  ENDP
  1930.  
  1931. ; ------------ Poner en mayúsculas los parámetros.
  1932.  
  1933. param_mays     PROC
  1934.                MOV   BX,81h
  1935. otra_mays:     MOV   AL,[BX]
  1936.                CMP   AL,13
  1937.                JNE   mays_mas
  1938.                RET
  1939. mays_mas:      CMP   AL,'a'
  1940.                JB    mayusc_ok
  1941.                CMP   AL,'z'
  1942.                JA    mayusc_ok
  1943.                SUB   AL,32
  1944. mayusc_ok:     MOV   [BX],AL
  1945.                INC   BX
  1946.                JMP   otra_mays
  1947. param_mays     ENDP
  1948.  
  1949. ; ------------ Saltar espacios, tabuladores,... buscando un parámetro.
  1950.  
  1951. saltar_esp:    MOV   AX,[BX]
  1952.                INC   BX
  1953.                CMP   AL,9              ; carácter tabulador
  1954.                JE    saltar_esp
  1955.                CMP   AL,32             ; espacio en blanco
  1956.                JE    saltar_esp
  1957.                CMP   AL,0Dh            ; fin de zona de parámetros
  1958.                JE    fin_param
  1959.                DEC   BX                ; puntero al primer carácter
  1960.                CLC                     ; hay parámetro
  1961.                RET
  1962. fin_param:     STC                     ; no hay parámetro
  1963.                RET
  1964.  
  1965. ; ------------ Ya está instalada otra versión distinta del programa.
  1966.  
  1967. error_version  PROC
  1968.                PUSH  ES
  1969.                LEA   DX,mal_ver_txt1
  1970.                CALL  print
  1971.                LES   DI,tsr_dir
  1972.                MOV   AL,':'
  1973.                MOV   CL,255
  1974.                CLD
  1975.                REPNE SCASB
  1976.                REPNE SCASB
  1977.                MOV   DL,ES:[DI]        ; número de versión
  1978.                MOV   AH,2
  1979.                INT   21h
  1980.                MOV   DL,'.'
  1981.                MOV   AH,2
  1982.                INT   21h
  1983.                MOV   DL,ES:[DI+2]      ; revisión
  1984.                MOV   AH,2
  1985.                INT   21h
  1986.                LEA   DX,mal_ver_txt2
  1987.                CALL  print
  1988.                POP   ES
  1989.                RET
  1990. error_version  ENDP
  1991.  
  1992. ; ------------ Inicializar ciertas variables. La memoria consumida
  1993. ;              se calcula para el peor de los casos, cuando el buffer
  1994. ;              residente ocupa incluso el doble debido al DMA.
  1995.  
  1996. inic_general   PROC
  1997.                CALL  inic_XMS          ; detectar controlador XMS
  1998.                MOV   AX,(bytes_resid+tbuffer*2+15)/16
  1999.                MOV   parrafos_resid,AX ; memoria máxima necesaria
  2000.                MOV   DL,0
  2001.                CALL  tipo_disco
  2002.                JC    err_drv
  2003.                AND   DL,DL
  2004.                JZ    err_drv             ; no hay disqueteras
  2005.                MOV   info_A.tipo_drv,AL  ; guardar tipo unidad A:
  2006.                CMP   DL,1
  2007.                JE    fin_inic            ; no existe unidad B:
  2008.                MOV   DL,1
  2009.                CALL  tipo_disco
  2010.                JC    fin_inic
  2011.                MOV   info_B.tipo_drv,AL  ; guardar tipo unidad B:
  2012.                RET
  2013. err_drv:       MOV   AX,MALBIOS
  2014.                OR    error,AX
  2015. fin_inic:      RET
  2016.  
  2017. tipo_disco:    PUSH  ES
  2018.                MOV   AH,8
  2019.                MOV   BL,0
  2020.                INT   13h
  2021.                JC    tipo_dsk_err
  2022.                MOV   AL,BL
  2023.                AND   AL,AL
  2024.                JZ    tipo_dsk_err
  2025.                CMP   AL,4
  2026.                JB    tipo_dsk_ok
  2027.                MOV   AL,4                ; 1.44/2.88 indistinto
  2028. tipo_dsk_ok:   CLC
  2029.                POP   ES
  2030.                RET
  2031. tipo_dsk_err:  STC
  2032.                POP   ES
  2033.                RET
  2034. inic_general   ENDP
  2035.  
  2036. ; ------------ Considerar presencia de controlador XMS.
  2037.  
  2038. inic_XMS       PROC
  2039.                PUSH  ES
  2040.                MOV   AX,352Fh
  2041.                INT   21h               ; dirección de INT 2Fh en ES:BX
  2042.                MOV   AX,ES
  2043.                POP   ES
  2044.                AND   AX,AX
  2045.                JZ    xms_ausente       ; apunta a 0000:XXXX (DOS 2.x)
  2046.                MOV   AX,4300h
  2047.                INT   2Fh               ; chequear presencia XMS
  2048.                CMP   AL,80h
  2049.                JNE   XMS_ausente       ; no instalado
  2050.                PUSH  ES
  2051.                MOV   AX,4310h
  2052.                INT   2Fh               ; sí: obtener su dirección
  2053.                MOV   XMS_off,BX        ; y preservarla
  2054.                MOV   XMS_seg,ES
  2055.                MOV   xms_ins,ON
  2056.                POP   ES
  2057.                RET
  2058. XMS_ausente:   MOV   xms_ins,OFF
  2059.                RET
  2060. inic_XMS       ENDP
  2061.  
  2062. ; ------------ Inicializar área «program_id» del programa residente.
  2063. ;              A la entrada, ES:DI = seg:off a donde será reubicado
  2064. ;              y CF=1 si se utiliza memoria superior XMS.
  2065.  
  2066. inicializa_id  PROC
  2067.                PUSHF
  2068.                MOV   segmento_real,ES  ; anotar segmento del bloque
  2069.                MOV   offset_real,DI    ; ídem con el offset
  2070.                MOV   AX,parrafos_resid
  2071.                MOV   longitud_total,AX
  2072.                MOV   AL,1
  2073.                POPF                    ; CF=0: usar memoria UMB XMS
  2074.                JNC   info_ok
  2075.                DEC   AL                ; usar memoria convencional
  2076. info_ok:       OR    info_extra,AL
  2077.                RET
  2078. inicializa_id  ENDP
  2079.  
  2080. ; ------------ Comprobar si el programa ya reside en memoria. A la
  2081. ;              salida, CF=0 si programa ya reside, con «tsr_seg» y
  2082. ;              «tsr_off» inicializadas apuntando a la cadena de
  2083. ;              identificación de la copia residente.  Si CF=1, el
  2084. ;              programa no reside aún (AX=0) o reside pero en otra
  2085. ;              versión distinta (AX=1).
  2086.  
  2087. residente?     PROC
  2088.                XPUSH <CX, SI, DI, ES, AX>
  2089.                LEA   DI,autor_nom_ver  ; identificación del programa
  2090.                MOV   SI,DI
  2091.                MOV   AL,0
  2092.                MOV   CL,255
  2093.                CLD
  2094.                REPNE SCASB
  2095.                SUB   DI,SI
  2096.                MOV   CX,DI             ; tamaño autor+programa+versión
  2097.                MOV   AX,1492h
  2098.                MOV   ES,AX
  2099.                MOV   DI,1992h          ; ES:DI protocolo de búsqueda
  2100.                CALL  mx_find_tsr       ; buscar si está en memoria
  2101.                MOV   tsr_off,DI        ; anotar la dirección programa
  2102.                MOV   tsr_seg,ES        ; por si estaba instalado
  2103.                POP   AX
  2104.                JNC   resid_ok          ; CF=0 -> programa ya residente
  2105.                POP   ES
  2106.                PUSH  ES
  2107.                LEA   DI,autor_nom_ver
  2108.                MOV   SI,DI
  2109.                MOV   AL,':'
  2110.                MOV   CL,255
  2111.                REPNE SCASB
  2112.                REPNE SCASB
  2113.                SUB   DI,SI
  2114.                MOV   CX,DI             ; tamaño autor+programa
  2115.                MOV   AX,1492h
  2116.                MOV   ES,AX
  2117.                MOV   DI,1992h          ; ES:DI protocolo de búsqueda
  2118.                CALL  mx_find_tsr       ; buscar si está en memoria
  2119.                MOV   tsr_off,DI        ; anotar dirección del programa
  2120.                MOV   tsr_seg,ES        ; por si instalada otra versión
  2121.                MOV   AX,0
  2122.                JC    resid_ok          ; CF=1, AX=0 -> no residente
  2123.                MOV   AX,1
  2124.                STC                     ; CF=1, AX=1 -> sí: otra vers.
  2125. resid_ok:      XPOP  <ES, DI, SI, CX>
  2126.                RET
  2127. residente?     ENDP
  2128.  
  2129. ; ------------ Preservar vectores de interrupción previos.
  2130.  
  2131. preservar_INTs PROC
  2132.                XPUSH <ES, DI>
  2133.                LEA   DI,tabla_vectores
  2134.                MOV   CL,[DI-1]
  2135.                MOV   CH,0              ; CX vectores interceptados
  2136. otro_vector:   XPUSH <CX, DI>
  2137.                MOV   AH,35h
  2138.                MOV   AL,[DI]
  2139.                INT   21h               ; obtener vector de INT xx
  2140.                XPOP  <DI, CX>
  2141.                MOV   [DI+1],BX         ; anotar donde apunta
  2142.                MOV   [DI+3],ES
  2143.                ADD   DI,5
  2144.                LOOP  otro_vector       ; repetir con los restantes
  2145.                XPOP  <DI, ES>
  2146.                RET
  2147. preservar_INTs ENDP
  2148.  
  2149. ; ------------ Liberar espacio de entorno.
  2150.  
  2151. free_environ   PROC
  2152.                PUSH  ES
  2153.                MOV   ES,DS:[2Ch]       ; dirección del entorno
  2154.                MOV   AH,49h
  2155.                INT   21h               ; liberar espacio de entorno
  2156.                POP   ES
  2157.                RET
  2158. free_environ   ENDP
  2159.  
  2160. ; ------------ Reservar bloque de memoria superior del nº párrafos AX,
  2161. ;              devolviendo en AX el segmento donde está. CF=1 si no
  2162. ;              está instalado el gestor XMS (AX=0) o hay un error (AL
  2163. ;              devuelve el código de error del controlador XMS).
  2164.  
  2165. UMB_alloc      PROC
  2166.                XPUSH <BX, CX, DX>
  2167.                CMP   xms_ins,ON
  2168.                JNE   no_umb_disp       ; no hay controlador XMS
  2169.                MOV   DX,AX             ; número de párrafos
  2170.                MOV   AH,10h            ; solicitar memoria superior
  2171.                CALL  gestor_XMS
  2172.                CMP   AX,1              ; ¿ha ido todo bien?
  2173.                MOV   AX,BX             ; segmento UMB/código de error
  2174.                JNE   XMS_fallo         ; fallo
  2175.                XPOP  <DX, CX, BX>      ; ok
  2176.                CLC
  2177.                RET
  2178. no_umb_disp:   MOV   AX,0
  2179. XMS_fallo:     XPOP  <DX, CX, BX>
  2180.                STC
  2181.                RET
  2182. UMB_alloc      ENDP
  2183.  
  2184. ; ------------ Reservar memoria superior, con DOS 5.0, del tamaño
  2185. ;              solicitado (AX párrafos). Si no hay bastante CF=1,
  2186. ;              en caso contrario devuelve el segmento en AX.
  2187.  
  2188. UPPER_alloc    PROC
  2189.                PUSH  AX
  2190.                MOV   AH,30h
  2191.                INT   21h
  2192.                CMP   AL,5
  2193.                POP   AX
  2194.                JAE   UPPER_existe
  2195.                STC
  2196.                JMP   UPPER_fin         ; necesario DOS 5.0 mínimo
  2197. UPPER_existe:  PUSH  AX                ; preservar párrafos...
  2198.                MOV   AX,5800h
  2199.                INT   21h
  2200.                MOV   alloc_strat,AX    ; preservar estrategia
  2201.                MOV   AX,5802h
  2202.                INT   21h
  2203.                MOV   umb_state,AL      ; preservar estado UMB
  2204.                MOV   AX,5803h
  2205.                MOV   BX,1
  2206.                INT   21h               ; conectar cadena UMB's
  2207.                MOV   AX,5801h
  2208.                MOV   BX,41h
  2209.                INT   21h               ; High Memory best fit
  2210.                POP   BX                ; ...párrafos requeridos
  2211.                MOV   AH,48h
  2212.                INT   21h               ; asignar memoria
  2213.                PUSHF
  2214.                PUSH  AX                ; guardado el resultado
  2215.                MOV   AX,5801h
  2216.                MOV   BX,alloc_strat
  2217.                INT   21h               ; restaurar estrategia
  2218.                MOV   AX,5803h
  2219.                MOV   BL,umb_state
  2220.                XOR   BH,BH
  2221.                INT   21h               ; restaurar estado cadena UMB
  2222.                POP   AX
  2223.                POPF
  2224. upper_fin:     RET
  2225. UPPER_alloc    ENDP
  2226.  
  2227. ; ------------ Manipular PID para independizar el bloque de memoria
  2228. ;              superior del programa y dejarlo residente. ES apunta
  2229. ;              al segmento y DS al PSP del programa principal.
  2230.  
  2231. upper_fork     PROC
  2232.                XPUSH <DS, ES>
  2233.                MOV   AX,ES
  2234.                DEC   AX
  2235.                MOV   ES,AX
  2236.                INC   AX
  2237.                MOV   WORD PTR ES:[1],AX      ; manipular PID
  2238.                MOV   WORD PTR ES:[16],20CDh  ; simular PSP
  2239.                MOV   AX,DS
  2240.                DEC   AX
  2241.                MOV   DS,AX
  2242.                MOV   CX,8
  2243.                MOV   SI,CX
  2244.                MOV   DI,CX
  2245.                CLD
  2246.                REP   MOVSB             ; copiar nombre de programa
  2247.                XPOP  <ES, DS>
  2248.                RET
  2249. upper_fork     ENDP
  2250.  
  2251. ; ------------ Reubicar programa residente a su dirección definitiva.
  2252.  
  2253. reubicar_prog  PROC
  2254.                PUSH  DI
  2255.                LEA   SI,ini_residente
  2256.                MOV   CX,bytes_resid
  2257.                CLD
  2258.                ADD   SI,2              ; no copiar primera palabra
  2259.                ADD   DI,2              ; respetar primera palabra
  2260.                SUB   CX,2
  2261.                REP   MOVSB
  2262.                POP   DI
  2263.                RET
  2264. reubicar_prog  ENDP
  2265.  
  2266. ; ------------ desviar vectores de interrupción a las nuevas rutinas.
  2267. ;              Se tendrá en cuenta que está ensambladas para correr en
  2268. ;              un offset inicial (100h) y que el offset real en que
  2269. ;              han sido instaladas está en DI. Por ello, CS ha de
  2270. ;              desplazarse (100h-DI)/16 unidades atrás (DI se supone
  2271. ;              múltiplo de 16). El segmento inicial es ES.
  2272.  
  2273. activar_INTs   PROC
  2274.                XPUSH <CX, DS>          ; preservar DS para el retorno
  2275.                MOV   AX,100h
  2276.                SUB   AX,DI             ; AX = 100h-DI
  2277.                MOV   CL,4
  2278.                SHR   AX,CL             ; AX = (100h-DI)/16
  2279.                MOV   CX,ES
  2280.                SUB   CX,AX
  2281.                MOV   tsr_seg,CX
  2282.                MOV   DS,CX
  2283.                LEA   SI,offsets_ints
  2284.                MOV   CX,CS:[SI]        ; CX vectores a desviar
  2285.                ADD   SI,2
  2286. desvia_otro:   MOV   AL,CS:[SI]        ; número del vector en curso
  2287.                MOV   DX,CS:[SI+1]      ; obtener offset
  2288.                MOV   AH,25h
  2289.                INT   21h               ; desviar INT xx a DS:DX
  2290.                ADD   SI,3
  2291.                LOOP  desvia_otro
  2292.                XPOP  <DS, CX>
  2293.                RET
  2294. activar_INTs   ENDP
  2295.  
  2296. ; ------------ Buscar entrada no usada en la interrupción Multiplex.
  2297. ;              A la salida, CF=1 si no hay hueco (ya hay 64 programas
  2298. ;              residentes instalados con esta técnica). Si CF=0, se
  2299. ;              devuelve en AH un valor de entrada libre en la INT 2Fh.
  2300.  
  2301. mx_get_handle  PROC
  2302.                MOV   AH,0C0h
  2303. mx_busca_hndl: PUSH  AX
  2304.                MOV   AL,0
  2305.                INT   2Fh
  2306.                CMP   AL,0FFh
  2307.                POP   AX
  2308.                JNE   mx_si_hueco
  2309.                INC   AH
  2310.                JNZ   mx_busca_hndl
  2311.                STC
  2312.                RET
  2313. mx_si_hueco:   CLC
  2314.                RET
  2315. mx_get_handle  ENDP
  2316.  
  2317. ; ------------ Buscar un TSR por la interrupción Multiplex. A la
  2318. ;              entrada, DS:SI cadena de identificación del programa
  2319. ;              (CX bytes) y ES:DI protocolo de búsqueda (normalmente
  2320. ;              1492h:1992h). A la salida, si el TSR ya está instalado,
  2321. ;              CF=0 y ES:DI apunta a la cadena de identificación del
  2322. ;              mismo. Si no, CF=1 y ningún registro alterado.
  2323.  
  2324. mx_find_tsr    PROC
  2325.                MOV   AH,0C0h
  2326. mx_rep_find:   XPUSH <AX, CX, SI, DS, ES, DI>
  2327.                MOV   AL,0
  2328.                PUSH  CX
  2329.                INT   2Fh
  2330.                POP   CX
  2331.                CMP   AL,0FFh
  2332.                JNE   mx_skip_hndl      ; no hay TSR ahí
  2333.                CLD
  2334.                PUSH  DI
  2335.                REP   CMPSB             ; comparar identificación
  2336.                POP   DI
  2337.                JE    mx_tsr_found      ; programa buscado hallado
  2338. mx_skip_hndl:  XPOP  <DI, ES, DS, SI, CX, AX>
  2339.                INC   AH
  2340.                JNZ   mx_rep_find
  2341.                STC
  2342.                RET
  2343. mx_tsr_found:  ADD   SP,4              ; «sacar» ES y DI de la pila
  2344.                XPOP  <DS, SI, CX, AX>
  2345.                CLC
  2346.                RET
  2347. mx_find_tsr    ENDP
  2348.  
  2349. ; ------------ Eliminar TSR del convenio si es posible. A la entrada,
  2350. ;              en AH se indica la entrada Multiplex; a la salida, CF=1
  2351. ;              si fue imposible y CF=0 si se pudo. Se corrompen todos
  2352. ;              los registros salvo los de segmento. En caso de fallo
  2353. ;              al desinstalar, AL devuelve el vector «culpable».
  2354.  
  2355. mx_unload      PROC
  2356.                PUSH  ES
  2357.                CALL  mx_ul_tsrcv?
  2358.                JNC   mx_ul_able
  2359.                POP   ES
  2360.                RET
  2361. mx_ul_able:    XOR   AL,AL
  2362.                XCHG  AH,AL
  2363.                MOV   BP,AX             ; BP=entrada Multiplex del TSR
  2364.                MOV   CX,2
  2365. mx_ul_pasada:  PUSH  CX                ; siguiente pasada
  2366.                LEA   SI,tabla_vectores
  2367.                MOV   CL,ES:[SI-1]
  2368.                MOV   CH,0              ; CX = nº vectores
  2369. mx_ul_masvect: POP   AX
  2370.                PUSH  AX                ; pasada en curso
  2371.                DEC   AL
  2372.                PUSH  CX
  2373. mx_ul_2f:      MOV   AL,ES:[SI]        ; vector en curso
  2374.                JNZ   mx_ul_pasok
  2375.                CMP   CX,1              ; ¿último vector?
  2376.                JNE   mx_ul_noult
  2377.                MOV   AL,2Fh
  2378.                LEA   SI,tabla_vectores
  2379. mx_ul_busca2f: CMP   ES:[SI],AL        ; ¿INT 2Fh?
  2380.                JE    mx_ul_pasok
  2381.                ADD   SI,5
  2382.                JMP   mx_ul_busca2f
  2383. mx_ul_noult:   CMP   AL,2Fh            ; ¿restaurar INT 2Fh?
  2384.                JNE   mx_ul_pasok
  2385.                ADD   SI,5
  2386.                JMP   mx_ul_2f
  2387. mx_ul_pasok:   XPUSH <ES, AX>
  2388.                MOV   AH,0
  2389.                SHL   AX,1
  2390.                SHL   AX,1
  2391.                DEC   AX
  2392.                MOV   CS:mx_ul_tsroff,AX
  2393.                MOV   CS:mx_ul_tsrseg,0 ; apuntar a tabla vectores
  2394.                POP   AX
  2395.                PUSH  AX
  2396.                MOV   AH,35h
  2397.                INT   21h               ; vector en ES:BX
  2398.                POP   AX
  2399.                MOV   CL,4
  2400.                SHR   BX,CL
  2401.                MOV   DX,ES
  2402.                ADD   DX,BX             ; INT xx en DX (aprox.)
  2403.                MOV   AH,0C0h
  2404. mx_ul_masmx:   CALL  mx_ul_tsrcv?
  2405.                JNC   mx_ul_tsrcv
  2406.                JMP   mx_ul_otro
  2407. mx_ul_tsrcv:   PUSH  ES:[DI-16]        ; ...TSR del convenio en ES:DI
  2408.                PUSH  ES:[DI-12]
  2409.                MOV   DI,ES:[DI-8]      ; offset a la tabla de vectores
  2410.                MOV   CL,ES:[DI-1]
  2411.                MOV   CH,0              ; número de vectores en CX
  2412. mx_ul_buscav:  CMP   AL,ES:[DI]
  2413.                JE    mx_ul_usavect     ; este TSR usa vector analizado
  2414.                ADD   DI,5
  2415.                LOOP  mx_ul_buscav
  2416.                ADD   SP,4              ; no lo usa
  2417.                JMP   mx_ul_otro
  2418. mx_ul_usavect: XPOP  <CX, BX>          ; tamaño y segmento del TSR
  2419.                CMP   DX,BX
  2420.                JB    mx_ul_otro        ; la INT xx no le apunta
  2421.                ADD   BX,CX
  2422.                CMP   DX,BX
  2423.                JA    mx_ul_otro        ; la INT xx le apunta
  2424.                PUSH  AX
  2425.                XOR   AL,AL
  2426.                XCHG  AH,AL
  2427.                CMP   AX,BP             ; ¿es el propio TSR?
  2428.                POP   AX
  2429.                JNE   mx_ul_chain       ; no
  2430.                XPOP  <ES, CX, BX>      ; sí: ¡posible reponer vector!
  2431.                XPUSH <BX, CX, ES>
  2432.                DEC   BX
  2433.                JNZ   mx_ul_norest      ; no es la segunda pasada
  2434.                POP   ES                ; segunda pasada...
  2435.                XPUSH <ES, DS>
  2436.                MOV   BX,CS:mx_ul_tsroff ; restaurar INT's
  2437.                MOV   DS,CS:mx_ul_tsrseg
  2438.                CLI
  2439.                MOV   CX,ES:[SI+1]
  2440.                MOV   [BX+1],CX
  2441.                MOV   CX,ES:[SI+3]
  2442.                MOV   [BX+3],CX
  2443.                STI
  2444.                POP   DS
  2445. mx_ul_norest:  XPOP  <ES, CX>
  2446.                ADD   SI,5              ; siguiente vector
  2447.                DEC   CX
  2448.                JZ    mx_unloadable     ; no más, ¡desinstal-ar/ado!
  2449.                JMP   mx_ul_masvect
  2450. mx_ul_chain:   MOV   CS:mx_ul_tsroff,DI ; ES:DI almacena la dirección
  2451.                MOV   CS:mx_ul_tsrseg,ES ; de la variable vector
  2452.                MOV   DX,ES:[DI+1]
  2453.                MOV   CL,4
  2454.                SHR   DX,CL
  2455.                MOV   CX,ES:[DI+3]
  2456.                ADD   DX,CX             ; INT xx en DX (aprox.)
  2457.                MOV   AH,0BFh
  2458. mx_ul_otro:    INC   AH                ; a por otro TSR
  2459.                JZ    mx_ul_exitnok     ; ¡se acabaron!
  2460.                JMP   mx_ul_masmx
  2461. mx_ul_exitnok: ADD   SP,6              ; equilibrar pila
  2462.                POP   ES
  2463.                STC
  2464.                RET                     ; imposible desinstalar
  2465. mx_unloadable: POP   CX
  2466.                DEC   CX
  2467.                JZ    mx_ul_exitok      ; desinstalado
  2468.                JMP   mx_ul_pasada      ; 1ª pasada exitosa: por la 2ª
  2469. mx_ul_exitok:  TEST  ES:info_extra,111b  ; ¿tipo de instalación?
  2470.                MOV   ES,ES:segmento_real ; segmento real del bloque
  2471.                JZ    mx_ul_freeml        ; cargado en RAM convencional
  2472.                CMP   xms_ins,ON
  2473.                JNE   mx_ul_freeml      ; no hay controlador XMS (¿?)
  2474.                MOV   DX,ES
  2475.                MOV   AH,11h
  2476.                CALL  gestor_XMS        ; liberar memoria superior
  2477.                POP   ES
  2478.                CLC
  2479.                RET
  2480. mx_ul_freeml:  MOV   AH,49h
  2481.                INT   21h               ; liberar bloque de memoria ES:
  2482.                POP   ES
  2483.                CLC
  2484.                RET
  2485. mx_ul_tsrcv?:  XPUSH <AX, ES, DI>       ; ¿es TSR del convenio?...
  2486.                MOV   DI,1492h
  2487.                MOV   ES,DI
  2488.                MOV   DI,1992h
  2489.                INT   2Fh
  2490.                CMP   AX,0FFFFh
  2491.                JNE   mx_ul_ncvexit
  2492.                CMP   WORD PTR ES:[DI-4],"#*"
  2493.                JNE   mx_ul_ncvexit
  2494.                CMP   WORD PTR ES:[DI-2],"*#"
  2495.                JNE   mx_ul_ncvexit
  2496.                ADD   SP,4              ; CF=0
  2497.                POP   AX
  2498.                RET
  2499. mx_ul_ncvexit: XPOP  <DI, ES, AX>      ; ...no es TSR del convenio
  2500.                STC                     ; CF=1
  2501.                RET
  2502. mx_ul_tsroff   DW    0
  2503. mx_ul_tsrseg   DW    0
  2504. mx_unload      ENDP
  2505.  
  2506. ; ------------ Inicializar variable idioma_sp según idioma del país.
  2507.  
  2508. habla_hispana? PROC
  2509.                XPUSH <AX, BX, CX, DX, BP>
  2510.                MOV   AH,30h
  2511.                INT   21h
  2512.                XCHG  AH,AL             ; AX = versión del DOS
  2513.                MOV   BP,AX
  2514.                MOV   idioma_sp,OFF     ; supuesto de habla no hispana
  2515.                CMP   BP,200h
  2516.                JB    habla_ok
  2517.                LEA   DX,buffer_aux
  2518.                MOV   AX,3800h
  2519.                INT   21h               ; obtener información del pais
  2520.                CMP   BP,20Bh
  2521.                JE    habla_ax          ; DOS 2.11: AX cód. telefónico
  2522.                CMP   BP,300h
  2523.                JB    habla_ok          ; 2.x excepto 2.11: mala suerte
  2524.                MOV   AX,BX
  2525. habla_ax:      LEA   BX,paises_sp-2
  2526.                MOV   CX,numpaises_sp
  2527. habla_sp?:     ADD   BX,2
  2528.                CMP   AX,[BX]
  2529.                JE    habla_hispana
  2530.                LOOP  habla_sp?
  2531. habla_ok:      MOV   AL,param_i
  2532.                XOR   idioma_sp,AL      ; considerar parámetro /I
  2533.                XPOP  <BP, DX, CX, BX, AX>
  2534.                RET
  2535. habla_hispana: MOV   idioma_sp,ON      ; país de habla hispana
  2536.                MOV   AL,param_i
  2537.                XOR   idioma_sp,AL      ; considerar parámetro /I
  2538.                XPOP  <BP, DX, CX, BX, AX>
  2539.                RET
  2540. habla_hispana? ENDP
  2541.  
  2542. ; ------------ Imprimir cadena en DS:DX delimitada por un 0 ó un 255.
  2543. ;              Si hay que imprimir en inglés se toma la cadena que va
  2544. ;              después si ésta acaba en 255 (si acaba en 0, no hay
  2545. ;              distinción entre mensaje castellano e inglés).
  2546.  
  2547. print          PROC
  2548.                XPUSH <AX, BX, CX, DX>
  2549. pr_decidir:    CMP   idioma_sp,OFF
  2550.                JE    usar_uk
  2551.                CMP   idioma_sp,ON
  2552.                JE    usar_sp
  2553.                PUSH  DX
  2554.                CALL  habla_hispana?         ; determinar lengua
  2555.                POP   DX
  2556.                JMP   pr_decidir
  2557. usar_uk:       MOV   BX,DX
  2558.                DEC   BX
  2559. usar_uk?:      INC   BX
  2560.                CMP   BYTE PTR [BX],0
  2561.                JE    usar_sp                ; acaba en 0: no traducir
  2562.                CMP   BYTE PTR [BX],255
  2563.                JNE   usar_uk?
  2564.                LEA   DX,[BX+1]              ; acaba en 255: traducir
  2565. usar_sp:       MOV   BX,DX
  2566.                DEC   BX
  2567. print_cad:     INC   BX
  2568.                CMP   BYTE PTR [BX],0
  2569.                JE    prlong_ok
  2570.                CMP   BYTE PTR [BX],255
  2571.                JNE   print_cad              ; calcular longitud
  2572. prlong_ok:     MOV   CX,BX
  2573.                SUB   CX,DX
  2574.                MOV   AH,40h
  2575.                MOV   BX,1
  2576.                INT   21h
  2577.                XPOP  <DX, CX, BX, AX>
  2578.                RET
  2579. print          ENDP
  2580.  
  2581. ; ------------ Informar al usuario.
  2582.  
  2583. info           PROC
  2584.                CMP   param_ayuda,ON    ; ¿solicitud de ayuda?
  2585.                JNE   info_normal
  2586.                LEA   DX,ayuda_txt
  2587.                CALL  print
  2588.                JMP   fin_info
  2589. info_normal:   LEA   DX,programa_txt
  2590.                CALL  print
  2591.                TEST  error,0FFFFh
  2592.                JZ    info_ins          ; no hay error
  2593.                JMP   info_err          ; lo hay: informar del mismo
  2594. info_ins:      LEA   DX,instalado_txt
  2595.                TEST  accion,INSTALADO  ; ¿informar "instalado"?
  2596.                JNZ   acc_ok
  2597.                LEA   DX,ya_instal_txt
  2598.                TEST  accion,YAINSTALADO  ; ¿informar "ya instalado"?
  2599.                JNZ   acc_ok
  2600.                LEA   DX,des_ok_txt     ; informar "desinstalado"
  2601.                CALL  print
  2602.                JMP   fin_info
  2603. acc_ok:        CALL  print             ; instalado/ya instalado...
  2604.                LEA   DX,dma_cross_txt
  2605.                TEST  accion,BUFFERPLUS ; ¿el DMA cruzaba frontera?
  2606.                JZ    dma_ok
  2607.                CALL  print
  2608. dma_ok:        JMP   fin_info
  2609. info_err:      LEA   DX,mal_pc_txt
  2610.                TEST  error,MALPC       ; ¿no es ordenador adecuado?
  2611.                JZ    otroerr1
  2612.                CALL  print
  2613. otroerr1:      LEA   DX,mal_dos_txt
  2614.                TEST  error,MALDOS      ; ¿DOS incorrecto?
  2615.                JZ    otroerr21
  2616.                CALL  print
  2617. otroerr21:     LEA   DX,mal_bios_txt
  2618.                TEST  error,MALBIOS     ; ¿BIOS obsoleta?
  2619.                JZ    otroerr22
  2620.                CALL  print
  2621. otroerr22:     LEA   DX,mal_drv_txt
  2622.                TEST  error,MALDRV      ; ¿Unidades de doble?
  2623.                JZ    otroerr23
  2624.                CALL  print
  2625. otroerr23:     LEA   DX,err_sintax_txt
  2626.                TEST  error,MALSINTAX   ; ¿error de sintaxis?
  2627.                JZ    otroerr3
  2628.                CALL  print
  2629. otroerr3:      LEA   DX,imp_desins_txt
  2630.                TEST  error,NOINSTALADO ; no instalado, ¿piden
  2631.                JZ    otroerr4          ; desinstalar?
  2632.                CALL  print
  2633. otroerr4:      LEA   DX,des_no_ok_txt
  2634.                TEST  error,IMPDESINS   ; ¿imposible desinstalar?
  2635.                JZ    otroerr5
  2636.                CALL  print
  2637. otroerr5:      LEA   DX,inst_sys_txt
  2638.                TEST  error,INSTALADOSYS  ; ¿instalada versión *.SYS?
  2639.                JZ    otroerr5x
  2640.                CALL  print
  2641. otroerr5x:     TEST  error,NOINSTALABLE  ; ¿versión incorrecta?
  2642.                JZ    otroerr6
  2643.                CALL  error_version
  2644. otroerr6:      LEA   DX,nocabe_txt
  2645.                TEST  error,MX64FULL    ; ¿imposible instalar?
  2646.                JZ    otroerr8
  2647.                CALL  print
  2648. otroerr8:      LEA   DX,win_ld_txt
  2649.                TEST  error,WIN_LOAD    ; ¿instalar desde Windows?
  2650.                JZ    otroerr9
  2651.                CALL  print
  2652. otroerr9:      LEA   DX,win_ul_txt
  2653.                TEST  error,WIN_UNLOAD  ; ¿desinstalar desde Windows?
  2654.                JZ    fin_info
  2655.                CALL  print
  2656. fin_info:      RET
  2657. info           ENDP
  2658.  
  2659. ; ------------ Comprobar que la configuración es la adecuada. Para
  2660. ;              saber si la INT 13h de este ordenador acaba llamando a
  2661. ;              la INT 40h, se desvía la INT 40h y se provoca un inocuo
  2662. ;              reset de disquetes vía INT 13h para comprobar si pasa
  2663. ;              por la INT 40h.
  2664.  
  2665. pc_ok?         PROC
  2666.                CALL  test_i40
  2667.                TEST  accion,I40
  2668.                JZ    test_dos          ; no soportada la INT 40h
  2669.                MOV   nueva_i13,40h
  2670.                MOV   vieja_i13,40h     ; usar INT 40 en vez de INT 13
  2671. test_dos:      MOV   AH,30h
  2672.                INT   21h
  2673.                XCHG  AH,AL
  2674.                CMP   AX,31Eh           ; ¿DOS 3.30 o superior?
  2675.                MOV   AX,MALDOS
  2676.                JB    pc_nok
  2677.                CALL  testAT
  2678.                MOV   AX,MALPC
  2679.                JC    pc_nok
  2680.                TEST  error,MALBIOS
  2681.                JNZ   pc_ok             ; con ese error vale
  2682.                MOV   AX,MALDRV
  2683.                CMP   info_A.tipo_drv,2 ; ¿unidad A: de 1.2?
  2684.                JE    pc_ok
  2685.                CMP   info_A.tipo_drv,4 ; ¿unidad A: de 1.44 ó 2.88?
  2686.                JAE   pc_ok
  2687.                CMP   info_B.tipo_drv,2 ; ¿unidad B: de 1.2?
  2688.                JE    pc_ok
  2689.                CMP   info_B.tipo_drv,4 ; ¿unidad B: de 1.44 ó 2.88?
  2690.                JAE   pc_ok
  2691. pc_nok:        OR    error,AX
  2692. pc_ok:         TEST  error,MALPC
  2693.                JZ    pc_ok?_ret
  2694.                AND   error,MALPC       ; ese error basta
  2695. pc_ok?_ret:    RET
  2696. pc_ok?         ENDP
  2697.  
  2698.                ; --- Comprobar si la INT 40h está en uso
  2699.  
  2700. test_i40:      XPUSH <DS, ES>          ; *
  2701.                MOV   AX,3540h
  2702.                INT   21h
  2703.                XPUSH <ES, BX>          ; vector de INT 40h original
  2704.                LEA   DX,i40_aux
  2705.                MOV   AX,2540h
  2706.                INT   21h               ; establecer nueva INT 40h
  2707.                XOR   AX,AX
  2708.                MOV   DL,0
  2709.                INT   13h               ; reset de disco
  2710.                XPOP  <DX, DS>
  2711.                MOV   AX,2540h
  2712.                INT   21h               ; restaurar INT 40h original
  2713.                XPOP  <ES, DS>          ; *
  2714.                RET
  2715.  
  2716. i40_aux        PROC
  2717.                OR    CS:accion,I40     ; sí utilizada INT 40h
  2718.                IRET                    ; desde la INT 13h
  2719. i40_aux        ENDP
  2720.  
  2721.                ; --- Detectar 286 ó superior.
  2722.  
  2723. testAT         PROC
  2724.                PUSH  AX
  2725.                PUSHF
  2726.                POP   AX
  2727.                OR    AH,70h        ; intentar activar bit 12, 13 ó 14
  2728.                PUSH  AX            ; del registro de estado
  2729.                POPF
  2730.                PUSHF
  2731.                POP   AX
  2732.                AND   AH,0F0h
  2733.                CMP   AH,0F0h
  2734.                JE    testedAT
  2735.                STC
  2736. testedAT:      CMC                 ; CF = 0 en AT y 1 en PC/XT
  2737.                POP   AX
  2738.                RET
  2739. testAT         ENDP
  2740.  
  2741. ; ------------ Comprobar que el buffer para el DMA en la copia
  2742. ;              residente no cruza una frontera. En ese caso se emplea
  2743. ;              otro buffer ubicado tras el habitual, lo que aumenta el
  2744. ;              consumo de memoria de este área. A la entrada AX apunta
  2745. ;              al segmento que contendrá el buffer y BX el offset. Si
  2746. ;              no se produce el cruce, se disminuye «parrafos_resid»
  2747. ;              para economizar memoria; incluso aunque se produzca,
  2748. ;              esta variable se reduce en lo posible. A la vuelta,
  2749. ;              «parrafos_resid» y BX indican la memoria definitiva
  2750. ;              ocupada por el programa.
  2751.  
  2752. testDMA        PROC
  2753.                XPUSH <AX, CX, DX>
  2754.                MOV   CX,16
  2755.                MUL   CX
  2756.                ADD   AX,BX
  2757.                ADC   DX,0              ; DX:AX = dirección 20 bits
  2758.                MOV   CX,DX
  2759.                PUSH  AX
  2760.                ADD   AX,tbuffer-1      ; buffer para el mayor sector
  2761.                ADC   DX,0
  2762.                POP   AX
  2763.                MOV   BX,parrafos_resid
  2764.                SUB   BX,tbuffer/16     ; mejor supuesto posible
  2765.                CMP   DX,CX
  2766.                JE    dmatested
  2767.                NEG   AX
  2768.                ADD   AX,15
  2769.                MOV   CL,4
  2770.                SHR   AX,CL
  2771.                ADD   BX,AX             ; consumo adicional
  2772.                SHL   AX,CL
  2773.                ADD   buffer,AX         ; nueva posición del buffer
  2774.                OR    accion,BUFFERPLUS ; aviso al usuario
  2775. dmatested:     MOV   parrafos_resid,BX
  2776.                XPOP  <DX, CX, AX>
  2777.                RET
  2778. testDMA        ENDP
  2779.  
  2780. ; ------------ Desde Windows, no se permite instalar o desinstalar 2M.
  2781.  
  2782. testWin        PROC
  2783.                PUSH  AX
  2784.                CMP   param_w,ON        ; ¿se indicó parámetro /W?
  2785.                JE    fin_testWin
  2786.                MOV   AX,1600h
  2787.                INT   2Fh
  2788.                AND   AL,AL             ; ¿Windows en modo extendido?
  2789.                JZ    noWinEnh
  2790.                CMP   AL,80h            ; ¿Windows en modo extendido?
  2791.                JE    noWinEnh
  2792. siWin:         STC                     ; estamos dentro de Windows
  2793.                JMP   fin_testWin
  2794. noWinEnh:      MOV   AX,4680h
  2795.                INT   2Fh
  2796.                AND   AX,AX
  2797.                JZ    siWin             ; Windows en modo real/estándar
  2798. fin_testWin:   POP   AX
  2799.                RET                     ; CF=1 si dentro de Windows
  2800. testWin        ENDP
  2801.  
  2802. ; ***********************************************
  2803. ; *                                             *
  2804. ; *   D A T O S    N O    R E S I D E N T E S   *
  2805. ; *                                             *
  2806. ; ***********************************************
  2807.  
  2808. ON             EQU   1         ; constantes booleanas
  2809. OFF            EQU   0
  2810.  
  2811. ; ------------ Gestión de memoria y control de instalación.
  2812.  
  2813. xms_ins        DB    OFF       ; a ON si presente controlador XMS
  2814. gestor_XMS     LABEL DWORD     ; dirección del controlador XMS
  2815. XMS_off        DW    0
  2816. XMS_seg        DW    0
  2817.  
  2818. parrafos_resid DW    ?         ; párrafos de memoria consumidos
  2819. alloc_strat    DW    0         ; estrategia asignación (DOS 5)
  2820. umb_state      DB    0         ; estado de bloques UMB (DOS 5)
  2821.  
  2822. tsr_dir        LABEL DWORD     ; dirección de la copia residente
  2823. tsr_off        DW    0
  2824. tsr_seg        DW    0
  2825.  
  2826. mem640         DW    0         ; párrafos de memoria convencional
  2827.  
  2828. offsets_ints   DW    2         ; número de vectores interceptados
  2829. nueva_i13      DB    13h       ; tabla de offsets de los vectores
  2830.                DW    ges_int13 ; de interrupción interceptados
  2831.                DB    2Fh
  2832.                DW    ges_int2F
  2833.  
  2834. param_ml       DB    OFF       ; a ON si se indicó parámetro /ML
  2835. param_u        DB    OFF       ; a ON si se indicó parámetro /U
  2836. param_i        DB    OFF       ; a ON si se indicó parámetro /I
  2837. param_w        DB    OFF       ; a ON si se indicó parámetro /W
  2838. param_ayuda    DB    OFF       ; a ON si se indicó /? /H ó ?
  2839.  
  2840. MALPC          EQU    1        ; Códigos de error
  2841. MALDOS         EQU    2
  2842. MALBIOS        EQU    4
  2843. MALDRV         EQU    8
  2844. MALSINTAX      EQU   16
  2845. NOINSTALADO    EQU   32
  2846. IMPDESINS      EQU   64
  2847. INSTALADOSYS   EQU  128
  2848. NOINSTALABLE   EQU  256
  2849. MX64FULL       EQU  512
  2850. WIN_LOAD       EQU 1024
  2851. WIN_UNLOAD     EQU 2048
  2852.  
  2853. INSTALADO      EQU   1         ; códigos de acción e información
  2854. YAINSTALADO    EQU   2
  2855. DESINSTALADO   EQU   4
  2856. BUFFERPLUS     EQU   8
  2857. I40            EQU  16
  2858.  
  2859. error          DW    0         ; variable para acumular errores
  2860. accion         DB    0         ; variable que indica lo sucedido
  2861.  
  2862. ; ------------ Códigos de modos y órdenes del DMA y del FDC.
  2863.  
  2864. F_READ         EQU   46h               ; modo DMA para lectura
  2865. F_WRITE        EQU   4Ah               ; modo DMA para escritura
  2866. F_VERIFY       EQU   42h               ; modo DMA para verificación
  2867. F_FORMAT       EQU   01001101b         ; orden de formateo del FDC
  2868.  
  2869. ; ------------ Otras variables.
  2870.  
  2871. idioma_sp      DB    5Ah       ; ni en ON ni en OFF al principio
  2872.  
  2873.                ; --- Código telefónico de países de
  2874.                ;     habla hispana (mucha o poca).
  2875.  
  2876. paises_sp      DW    54                ; Argentina
  2877.                DW    591               ; Bolivia
  2878.                DW    57                ; Colombia
  2879.                DW    506               ; Costa Rica
  2880.                DW    56                ; Chile
  2881.                DW    593               ; Ecuador
  2882.                DW    503               ; El Salvador
  2883.                DW    34                ; España
  2884.                DW    63                ; Filipinas
  2885.                DW    502               ; Guatemala
  2886.                DW    504               ; Honduras
  2887.                DW    212               ; Marruecos
  2888.                DW    52                ; México
  2889.                DW    505               ; Nicaragua
  2890.                DW    507               ; Panamá
  2891.                DW    595               ; Paraguay
  2892.                DW    51                ; Perú
  2893.                DW    80                ; Puerto Rico
  2894.                DW    508               ; República Dominicana
  2895.                DW    598               ; Uruguay
  2896.                DW    58                ; Venezuela
  2897.                DW    3                 ; genérico latinoamérica
  2898. numpaises_sp   EQU   ($-OFFSET paises_sp)/2
  2899.  
  2900. ; ------------ Texto.
  2901.  
  2902. programa_txt   DB    13,10,"  2M 2.1",0
  2903.  
  2904. instalado_txt  DB    " instalado.",13,10,255," installed.",13,10,0
  2905.  
  2906. ya_instal_txt  DB    " ya instalado.",13,10,255
  2907.                DB    " already installed.",13,10,0
  2908.  
  2909. nocabe_txt     DB    ": Instalación imposible.",13,10
  2910.                DB    "      Ya hay 64 programas residentes con la "
  2911.                DB    "misma técnica.",13,10,255
  2912.                DB    ": Unable to install.",13,10
  2913.                DB    "      There are already 64 TSR's with the same technique."
  2914.                DB    13,10,0
  2915.  
  2916. err_sintax_txt DB    13,10,"    - Parámetro(s) incorrecto(s)."
  2917.                DB    13,10,"      Ejecute 2M /? para obtener "
  2918.                DB    "ayuda.",13,10,7,255
  2919.                DB    13,10,"    - Incorrect option(s)."
  2920.                DB    13,10,"      Execute 2M /? to obtain help."
  2921.                DB    13,10,7,0
  2922.  
  2923. mal_pc_txt     DB    13,10,"    - Error: Necesario ordenador AT. Utilice 2MX en esta máquina.",13,10,255
  2924.                DB    13,10,"    - Error: Needs AT system. Use 2MX on this computer.",13,10,0
  2925.  
  2926. mal_dos_txt    DB    13,10,"    - Error: Necesaria versión DOS 3.30 ó posterior.",13,10,255
  2927.                DB    13,10,"    - Error: Needs at least DOS 3.30 or above.",13,10,0
  2928.  
  2929. mal_bios_txt   DB    13,10,"    - Error: No puedo detectar el tipo de las unidades. Instale 2M-ABIOS antes.",13,10,255
  2930.                DB    13,10,"    - Error: Impossible to detect drive types. Please install 2M-ABIOS before.",13,10,0
  2931.  
  2932. mal_drv_txt    DB    13,10,"    - Error: Necesaria(s) unidad(es) de alta densidad.",13,10,255
  2933.                DB    13,10,"    - Error: Needs high-density floppy drive(s).",13,10,0
  2934.  
  2935. dma_cross_txt  DB    "    - Nota: El buffer de E/S cruzaba una frontera de DMA y fue ampliado.",13,10
  2936.                DB    "            Cambie la ubicación en memoria de 2M para ahorrar unos bytes.",13,10,255
  2937.                DB    "    - Note: I/O buffer has been extended because 2M crosses a DMA boundary.",13,10
  2938.                DB    "            Modify the memory location of 2M to save a little memory.",13,10,0
  2939.  
  2940. win_ld_txt     DB    " *NO* instalado:",13,10,"    Este programa debe cargarse ANTES de entrar en Windows.",13,10,7,255
  2941.                DB    " *NOT* installed:",13,10,"    This program must be loaded before WINDOWS.",13,10,7,0
  2942. win_ul_txt     DB    " *NO* desinstalado:",13,10,"    Este programa debe descargarse fuera de Windows.",13,10,7,255
  2943.                DB    " *NOT* uninstalled:",13,10,"    This program must be uninstalled outside of WINDOWS.",13,10,7,0
  2944.  
  2945. mal_ver_txt1   DB    13,10,"    - Error: ya está instalada la versión ",255
  2946.                DB    13,10,"    - Error: Version ",0
  2947. mal_ver_txt2   DB    " de este programa.",13,10,7,255
  2948.                DB    " of this program is already installed.",13,10,7,0
  2949.  
  2950. des_ok_txt     DB    " desinstalado.",13,10,255," uninstalled.",13,10,0
  2951.  
  2952. des_no_ok_txt  DB    13,10,"    - Desinstalación imposible (se ha "
  2953.                DB    "instalado después un programa"
  2954.                DB    13,10,"      que no respeta el convenio y tiene "
  2955.                DB    "alguna interrupción común).",13,10,7,255
  2956.                DB    13,10,"    - Uninstall unavailable (it has been"
  2957.                DB    " installed before a program that"
  2958.                DB    13,10,"      not support CiriSOFT convention"
  2959.                DB    " and uses a common interrupt).",13,10,7,0
  2960.  
  2961. imp_desins_txt DB    13,10,"    - Programa aún no instalado: "
  2962.                DB    "imposible desinstalarlo.",13,10,255
  2963.                DB    13,10,"    - Program not installed: "
  2964.                DB    "impossible to uninstall.",13,10,0
  2965.  
  2966. inst_sys_txt   DB    13,10,"    - Instalado como controlador de dispositivo; no desinstalable.",13,10,255
  2967.                DB    13,10,"    - Installed as device driver: unable to uninstall.",13,10,0
  2968.  
  2969. ayuda_txt      LABEL BYTE
  2970. DB 13,10,10
  2971. DB "   2M 2.1 - CONTROLADOR PARA ACCESO A DISCOS FORMATEADOS CON TECNOLOGIA STV.",13,10
  2972. DB "     (c) 1994 Ciriaco García de Celis - Grupo Universitario de Informática.",13,10
  2973. DB "   C/Renedo, 2, 4-C; 47005 Valladolid (España) - ciri@gui.uva.es - 2:341/21.8",13,10,10
  2974. DB "                           Sintaxis:  2M [/ML] [/U]",13,10,10
  2975. DB " ■ 2M es un programa de tipo «CARDWARE», estando autorizada su distribución sin",13,10
  2976. DB " modificaciones y con carácter gratuito. Tras ejecutarlo, 2M se queda residente",13,10
  2977. DB " en memoria para dar soporte a disquetes de formato 2M creados por 2MF.",13,10,10
  2978. DB " ■ 2M precisa para operar:",13,10
  2979. DB " - Un equipo AT o superior con las disqueteras bien indicadas en el SETUP.",13,10
  2980. DB " - Al menos una disquetera de alta densidad (las de doble no son controladas).",13,10
  2981. DB " - 5152 bytes de memoria superior o en su defecto 5248 de memoria convencional.",13,10
  2982. DB " - Sistema operativo DOS y/o WINDOWS 3.X, otros entornos no son contemplados.",13,10
  2983. DB " - Utilidad 2MF para formatear los disquetes 2M.",13,10
  2984. DB " ■ 2M ofrece:",13,10
  2985. DB " - Soporte para disquetes de 902K (5¼-DD) hasta 3772K (3½-ED) en A: y en B:.",13,10
  2986. DB " - Soporte INT 13h para que los programas comerciales puedan formatear en 2M.",13,10
  2987. DB " - Soportados comandos DISKCOPY y ¡SYS! hacia discos destino con formato 2M.",13,10,10
  2988. DB " ■ Con /ML se fuerza la instalación en memoria convencional (2M se auto-instala",13,10
  2989. DB " automáticamente en memoria superior) y con /U se desinstala, si es posible.",13,10
  2990. DB 255
  2991.  
  2992. DB 13,10,10
  2993. DB "     2M 2.1  -  DISKETTE DRIVE CONTROLLER FOR STV DISKETTE FORMAT TECHNOLOGY.",13,10
  2994. DB "      (c) 1994 Ciriaco García de Celis - Grupo Universitario de Informática.",13,10
  2995. DB "    C/Renedo, 2, 4-C; 47005 Valladolid (Spain) - ciri@gui.uva.es - 2:341/21.8",13,10,10
  2996. DB "                            Syntax:  2M [/ML] [/U]",13,10,10
  2997. DB " ■ 2M is a CARDWARE utility; distribution is only authorized without charge and",13,10
  2998. DB " with no modify. Before being executed without any option,  the program becomes",13,10
  2999. DB " resident on memory, in order to handle 2M diskettes located in floppy drives.",13,10,10
  3000. DB " ■ 2M requires:",13,10
  3001. DB " - An AT or above computer with diskette drives correctly set on SETUP.",13,10
  3002. DB " - At least one high-density floppy drive (double ones are not drived).",13,10
  3003. DB " - 5152 bytes of upper memory or 5248 bytes of conventional memory instead.",13,10
  3004. DB " - DOS and/or WINDOWS 3.X system, another environs are not still supported.",13,10
  3005. DB " - 2MF utility program to format 2M diskettes.",13,10
  3006. DB " ■ 2M gives you:",13,10
  3007. DB " - Support for diskettes from 902K (5¼-DD) up to 3772K (3½-ED) on A: and B:.",13,10
  3008. DB " - INT 13h code improves a new 2M format service for high-level applications.",13,10
  3009. DB " - Supported DISKCOPY and ... SYS! commands into 2M diskette already formatted.",13,10,10
  3010. DB " ■ With /ML a conventional memory installation can be forced  (this is an upper",13,10
  3011. DB " memory self-installating utility) and /U tells 2M to uninstall, if possible.",13,10
  3012. DB 0
  3013.  
  3014. buffer_aux     DB    64 DUP (0)   ; buffer para alguna función del DOS
  3015.  
  3016. _PRINCIPAL     ENDS
  3017.                END   inicio
  3018.